A layout area renders a tree of controls — immutable C# records declared server-side through the Controls factory (MeshWeaver.Layout) and streamed to the browser as data. There is no client-side component code to write: you compose controls, the portal renders them, and they update reactively as the underlying data changes.
Two shapes cover the whole factory surface: parameterless controls are property getters (Controls.Stack, Controls.Tabs — no parentheses), and parameterised ones are methods (Controls.Label("…"), Controls.DataGrid(rows)). Children are attached with WithView(...) — one call per child — never with a children list. Every code cell on this page is executable: the result renders directly below the code, and Run re-executes it.
The control families — every control is created via Controls.* (plus Charts.* for charts) and composed with WithView.
The catalog at a glance
| Family | Factory members | Deep dive |
|---|---|---|
| Display | Label (+ typography helpers H1…H6, Body, Header), Badge, Icon, Markdown, Html, Title, CodeSample, Spacer |
Badges, Icons & Status |
| Containers & skins | Stack, LayoutGrid, Tabs, Splitter, Toolbar, Layout, Skins.Card |
Container Controls · Layout Grid |
| Inputs | Text, Number, Date, DateTime, CheckBox, Switch, Select, Combobox, Listbox, RadioGroup, Slider |
Form Input Controls |
| Actions | Button, MenuItem |
User Interface — click handlers |
| Data | DataGrid + PropertyColumnControl / TemplateColumnControl |
DataGrid |
| Charts & pivots | Charts.* (MeshWeaver.Layout.Chart), ToPivotGrid (MeshWeaver.Layout.Pivot) |
Charts at a glance · Pivot tricks |
| Feedback | Progress, Exception |
Badges, Icons & Status |
| Editors | Edit macro (EditorControl), MarkdownEditorControl, CodeEditorControl |
Editor · Code Editor |
| Mesh & navigation | MeshNodePicker, MeshSearch, SearchBox, FileBrowser, NavMenu, NavLink, NavGroup, LayoutArea, NamedArea |
Mesh Search · Navigation Menus |
One naming gotcha.
Controls.Text(...)is a text input (TextFieldControl), not display text. Read-only text goes throughControls.Label/ the typography helpers,Controls.Markdown, or — for genuinely pre-rendered markup only —Controls.Html.
Display — Label, Badge, Icon, Markdown
Display controls present read-only content: Label with typography helpers for text, Badge for status pills, Icon for the FluentIcons catalog, and Markdown for formatted prose. They are the leaves of most control trees.
Controls.Stack
.WithView(Controls.H4("Display controls"))
.WithView(Controls.Label("Every family on this page renders live in the kernel"))
.WithView(Controls.Stack
.WithOrientation(Orientation.Horizontal)
.WithHorizontalGap("8px")
.WithView(Controls.Badge("Released").WithAppearance("Accent"))
.WithView(Controls.Icon(FluentIcons.CheckmarkCircle()))
.WithView(Controls.Markdown("**Markdown** renders inline, too")))
See Badges, Icons & Status for the full display set, including Spacer and the Skins.Card skin.
Containers — Stack, Tabs, Toolbar, Splitter, LayoutGrid
Containers arrange other controls. WithView(control) appends a child; the optional second argument (s => s.WithLabel("…")) configures the child's slot — that is how tabs get labels, splitter panes get sizes, and grid items get column spans. Stack stacks vertically or horizontally, Tabs shows one panel at a time, Toolbar lays out action buttons, Splitter creates resizable panes, and LayoutGrid is the responsive 12-column system.
Controls.Tabs
.WithView(
Controls.Stack
.WithView(Controls.Markdown("`Stack` arranges children **vertically** by default; each `WithView` appends one child."))
.WithView(Controls.Toolbar
.WithView(Controls.Button("Refresh"))
.WithView(Controls.Button("Export"))),
s => s.WithLabel("Overview"))
.WithView(Controls.Label("Tabs show one panel at a time."), s => s.WithLabel("Details"))
See Container Controls for all five containers and the full WithView overload table, and Layout Grid for responsive breakpoints.
Inputs — Text, Number, Select, CheckBox, DateTime
Input controls bind a value two-way. In a real layout area the first argument is a JsonPointerReference into the area's data store (see Data Binding); in standalone demos it is a plain value. List selection takes Option<T> values; Select, Combobox, and Listbox share the same (data, options) shape and differ only in presentation.
var currencies = new[]
{
new Option<string>("CHF", "Swiss Franc (CHF)"),
new Option<string>("EUR", "Euro (EUR)")
};
Controls.Stack
.WithView(Controls.Text("Alice Example").WithLabel("Name"))
.WithView(Controls.Number(42, "Int32").WithLabel("Age"))
.WithView(Controls.Select("CHF", currencies).WithLabel("Currency"))
.WithView(Controls.CheckBox(true).WithLabel("Active"))
.WithView(Controls.DateTime(DateTime.Today).WithLabel("Start date"))
See Form Input Controls for every input rendered live — including Switch, TextArea, RadioGroup, and the mesh-node picker — and Property Attributes for the attributes that pick these controls automatically.
Data — DataGrid
Controls.DataGrid(rows) renders any collection as a sortable, resizable table — the standard way to render tabular data (never hand-built HTML). Columns are typed PropertyColumnControl<T> instances; property names are camelCase ("instrument" for Instrument). TemplateColumnControl puts an arbitrary control in every cell for action columns.
record Position(string Instrument, int Quantity, decimal Price);
var positions = new[]
{
new Position("Bond A", 100, 102.50m),
new Position("Equity B", 250, 48.30m),
new Position("Fund C", 75, 210.00m)
};
Controls.DataGrid(positions)
.WithColumn(new PropertyColumnControl<string> { Property = "instrument" }.WithTitle("Instrument"))
.WithColumn(new PropertyColumnControl<int> { Property = "quantity" }.WithTitle("Quantity"))
.WithColumn(new PropertyColumnControl<decimal> { Property = "price" }
.WithTitle("Price").WithAlign("end").WithFormat("N2"))
See DataGrid for pagination, virtualization, action columns, and the full option tables.
Charts and pivots
Charts live in MeshWeaver.Layout.Chart: the Charts.* factories turn plain arrays into column, bar, line, and pie charts, and the SliceBy(...).To*Chart(...) pipeline charts sliced datasets. The pivot twin (ToPivotGrid) folds flat facts into rows-by-X, columns-by-Y tables.
using MeshWeaver.Layout.Chart;
var revenue = new double[] { 480, 520, 610, 730 };
var quarters = new[] { "Q1", "Q2", "Q3", "Q4" };
Charts.Column(revenue, quarters, "Revenue (CHF k)")
.WithTitle("Quarterly revenue")
See Charts at a glance for the whole gallery, Pivot tricks for the pivot side, and Data Cubes for slicing real datasets.
Feedback — Progress
Controls.Progress(message, percentage) is the standard way to surface long-running work — imports, compiles, exports — in a layout area. In real use the percentage comes from an observable, so the bar advances as the operation reports progress; Controls.Exception(ex) renders a failure where a result would have gone.
Controls.Stack
.WithView(Controls.Progress("Exporting report…", 80))
.WithView(Controls.Progress("Compiling node type…", 45))
See Static vs Dynamic Views for feeding a control from a live stream.
Editors and forms
The Edit macro generates a complete form from a plain record — each property becomes the input its type and attributes dictate (string → text field, int → number field, bool → checkbox), with validation from standard data annotations. Never hand-build a form field-by-field when a record describes the shape.
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
record ContactForm
{
[Required]
[DisplayName("Full name")]
public string Name { get; init; } = "Alice Example";
[Range(18, 120)]
public int Age { get; init; } = 34;
[DisplayName("Subscribe to updates")]
public bool Subscribed { get; init; } = true;
}
Mesh.Edit(new ContactForm(), "catalogEditorDemo")
The rich editors are node-bound: they bind to a mesh node's content and auto-save through the node stream, so they need a layout-area host with a node path and cannot run standalone in a kernel cell:
// Node-bound — not runnable standalone: requires a mesh node to bind to.
new MarkdownEditorControl { Value = markdown }
.WithAutoSave(hubAddress, nodePath) // markdown w/ auto-save
MeshNodeContentEditorControl.ForType(nodePath, typeof(MyContent)) // typed content editor
See Editor for the macro's attribute-driven mapping and reactive output, Code Editor for the Monaco-based code editor, and Data Binding for how values flow.
Mesh controls — MeshNodePicker, MeshSearch
Mesh controls work directly against mesh content. MeshNodePicker is the standard picker whenever content references other content — it searches with query syntax and stores the selected node's path; never hand-build a select over node paths. MeshSearch and SearchBox provide free-text search surfaces, and FileBrowser navigates a content collection.
Controls.MeshNodePicker("Doc/GUI/DataGrid")
.WithQueries("namespace:Doc/GUI scope:descendants nodeType:Markdown")
.WithMaxResults(8)
.WithLabel("Pick a documentation page")
See Mesh Search for the search surface and Form Input Controls for the picker's query options.
Navigation — NavMenu, NavLink, NavGroup
Navigation menus compose from three controls: NavMenu (the container), NavGroup (a collapsible heading), and NavLink (a clickable link with optional FluentIcons icon). The URLs are ordinary mesh paths. To embed another hub's layout area inside a view, use Controls.LayoutArea(address, area); to reference a named slot within the current area, Controls.NamedArea(area).
Controls.NavMenu
.WithNavLink("GUI Overview", "/Doc/GUI", FluentIcons.Home())
.WithNavLink("Data Grid", "/Doc/GUI/DataGrid", FluentIcons.Table())
See Navigation Menus for grouped, collapsible menus and Layout Areas for how areas nest.
See also
- User Interface — how control trees travel to the browser and how click handlers run
- GUI documentation — the full GUI area index
- Data Binding —
JsonPointerReferenceand the reactive data pipeline behind every input