Component Contract
The props, responsibilities, and constraints a conforming view component must satisfy.
Component Contract
The component renders the data model the parser produced using the renderer declared in runtime (for example React, Solid, HTML, PDF, or a custom renderer). It is a presentation layer only — it
does not fetch, parse, or interpret raw source data itself.
Props
{
data, // whatever the parser returned
settings, // the manifest's settings, merged with any runtime overrides
source, // declared source file or directory metadata
resources // resolved paths under resources/
}Props delivery by renderer
The props object is always the same; what changes with runtime.renderer is how the runtime delivers it:
| Renderer | Component shape | Delivery |
|---|---|---|
react / solid | Module default-exporting a component function | Props are passed as the function's argument |
html | Static .html file | The runtime MUST inject the props as the global window.mview before the document's inline scripts execute |
html | JS/TS module | MUST default-export a (props) => string function returning the HTML |
A static .html component MUST read its data exclusively from window.mview:
<script>
const { data, settings } = window.mview;
// render with data
</script>It must not invent other globals (__MVIEW_DATA__, __DATA__, etc.) or fetch the source — the data arrives already parsed in window.mview.data. For the html renderer, window.mview is the props handoff — it does not count as "hidden global state" under the constraints below.
Interactivity
A component does not have to be interactive, but it can be:
react/solidcomponents may use state and handlers (useState,createSignal,onClick, etc.). A runtime that renders server-side MUST hydrate the component in the browser (client bundle + React'shydrateRoot/ Solid'shydrate) for that interactivity to work; otherwise the view stays static.htmlcomponents ship their own<script>tags and are interactive on their own.
React Example
import React from "react";
export default function Dashboard({ data, settings }: any) {
return (
<div style={{ padding: 24, fontFamily: "system-ui, sans-serif" }}>
<h1>Sales Dashboard</h1>
<section>
<h2>Total Sales</h2>
<strong>
{settings?.currency ?? "USD"} {data.totalSales.toLocaleString()}
</strong>
</section>
<section>
<h2>Sales by Month</h2>
<ul>
{data.salesByMonth.map((item: any) => (
<li key={item.month}>
{item.month}: {item.total}
</li>
))}
</ul>
</section>
<section>
<h2>Top Clients</h2>
<table>
<thead>
<tr><th>Client</th><th>Total</th></tr>
</thead>
<tbody>
{data.topClients.map((row: any, index: number) => (
<tr key={index}>
<td>{row.Client ?? row.client ?? "Unknown"}</td>
<td>{row.Total ?? row.total ?? 0}</td>
</tr>
))}
</tbody>
</table>
</section>
</div>
);
}A Solid, HTML, PDF, or other renderer receives the same conceptual model and must satisfy the same constraints; only the renderer-specific syntax or produced artifact changes.
Constraints (MUST / MUST NOT)
- The component MUST NOT read any source file directly. It only renders
data. - The component MUST NOT perform arbitrary
fetchcalls without the runtime granting explicit permission (see Security Model). - The component MUST render from
dataandsettingsalone — no hidden dependency on global state outside what it's handed as props. - The component SHOULD degrade gracefully (empty states, missing fields) rather than crashing the render tree, since
datareflects whatever the parser produced from a potentially changed source.
Why the split between parser and component
Keeping "understand the source" (parser) and "render the model" (component) as separate, independently regenerable artifacts is what makes an MView survive source changes without manual rewrites: if its schema or selection changes, only the parser needs to be regenerated; if the visual design needs to change, only the component does. See Regeneration for how a runtime should expose "Regenerate Parser" vs "Regenerate Component" as separate actions.
