Definition¶
SurfaceHost reserves a region of the current layout for another named UI surface.
Scope¶
Use SurfaceHost when a module shell and its content are rendered as separate surfaces. The shell surface owns navigation, framing, and the host region. The content surface is rendered into the host by matching the host surface_id.
Do not use SurfaceHost as a generic visual container. Use Column, Row, ContentArea, ScrollArea, or FlexContainer when the children belong to the same surface.
id and surface_id identify different things. id is the component id of the host inside the shell surface. surface_id is the name of the separate content surface that should mount inside that host.
host = sdk.ui.SurfaceHost("module_content_host", surface_id="module_content")
builder.add(host)The content route must use the same surface name:
builder.set_surface("module_content", shell_route="/module/shell")The required match is SurfaceHost.surface_id == builder.surface_id. The host component id does not need to match the surface id.
Do not rely on SurfaceHost.children for fallback labels or placeholder content. The web client renders SurfaceHost as a mount point and displays the hosted surface when it exists; local placeholder content should live outside the host.
Properties¶
| Property | Type | Default | Notes |
|---|---|---|---|
surface_id |
string |
required | Named surface mounted by this host. Treat it as static for the host. |
children |
list[str | Component] |
[] |
Inherited container field. Do not use it for cross-client placeholder content. |
style |
string |
client default | Standard style property. |
Static Host¶
host = sdk.ui.SurfaceHost(
"module_content_host",
surface_id="module_content",
)
builder.add(host)- kind: SurfaceHost
id: module_content_host
surface_id: module_contentModule Shell¶
A navigation shell normally places SurfaceHost beside module navigation. Wrap the host in the layout containers that define scrolling, splitting, or sizing.
nav = sdk.ui.Column("module_nav", ["nav_overview", "nav_settings"])
builder.add(nav)
sidebar = sdk.ui.Sidebar("module_sidebar", [nav.id])
sidebar.set_property("width", 220)
sidebar.set_property("max_width", 220)
builder.add(sidebar)
host = sdk.ui.SurfaceHost("module_content_host", surface_id="module_content")
builder.add(host)
content_scroll = sdk.ui.ScrollArea("module_content_scroll", [host.id])
content_scroll.set_property("stretch", True)
content_scroll.set_property("transparent", True)
builder.add(content_scroll)
shell = sdk.ui.Row("module_shell", [sidebar.id, content_scroll.id])
shell.set_property("align", "fill")
shell.set_property("stretch", True)
builder.add(shell)- kind: Row
id: module_shell
align: fill
stretch: true
children:
- kind: Sidebar
id: module_sidebar
width: 220
max_width: 220
children:
- kind: Column
id: module_nav
children:
- nav_overview
- nav_settings
- kind: ScrollArea
id: module_content_scroll
stretch: true
transparent: true
children:
- kind: SurfaceHost
id: module_content_host
surface_id: module_contentContent Surface¶
The content route must render into the same surface_id used by the host.
async def render(params: dict, session: dict):
builder = sdk.ui.Builder()
builder.set_surface("module_content", shell_route="/module/shell")
builder.add(sdk.ui.Title("content_title", "Overview", level=2))
builder.add(sdk.ui.Column("content_root", ["content_title"]))
return builderThe SDK helper prepare_shell_surface(...) creates the same surface setup and adds an empty content container:
content_id = sdk.ui.prepare_shell_surface(
builder,
surface_id="module_content",
shell_route="/module/shell",
content_component_id="module_content_root",
)Store Binding¶
Bind style when the host frame is driven by page or global store state.
builder.set_store("/components_test/surfacehost/style", "", scope="page")
host = sdk.ui.SurfaceHost("module_content_host", surface_id="module_content")
host.set_property(
"style",
bound.store("/components_test/surfacehost/style", scope="page", default=""),
)
builder.add(host)- kind: SurfaceHost
id: module_content_host
surface_id: module_content
style: {type: store, scope: page, path: /components_test/surfacehost/style, default: ""}Data Model Binding¶
Use data-model binding when the host style comes from the current surface data.
builder.set_data("/components_test/surfacehost_model/style", "")
host = sdk.ui.SurfaceHost("module_content_host", surface_id="module_content")
host.set_property(
"style",
bound.data("/components_test/surfacehost_model/style", default=""),
)
builder.add(host)- kind: SurfaceHost
id: module_content_host
surface_id: module_content
style: "@data/components_test/surfacehost_model/style"Property Updates¶
Declare capabilities before updating style directly. Do not update surface_id at runtime; create a host with the target surface id.
host = sdk.ui.SurfaceHost("module_content_host", surface_id="module_content")
host.allow("style.set")
builder.add(host)- kind: SurfaceHost
id: module_content_host
surface_id: module_content
capabilities: [style.set]Visibility And Permissions¶
show_if, hide_if, and required_permissions are available on SurfaceHost.
host.set_show_if({
"conditions": [
{"left": bound.store("/components_test/visibility/surfacehost_show", scope="page", default=True), "op": "==", "right": True}
]
})
host.set_hide_if({
"conditions": [
{"left": bound.store("/components_test/visibility/surfacehost_hide", scope="page", default=False), "op": "==", "right": True}
]
})
host.set_required_permissions(["components.layout.view"])- kind: SurfaceHost
id: surfacehost_show_if
surface_id: module_content
show_if:
conditions:
- left: {type: store, scope: page, path: /components_test/visibility/surfacehost_show, default: true}
op: "=="
right: true
- kind: SurfaceHost
id: surfacehost_hide_if
surface_id: module_content
hide_if:
conditions:
- left: {type: store, scope: page, path: /components_test/visibility/surfacehost_hide, default: false}
op: "=="
right: true
- kind: SurfaceHost
id: surfacehost_required_permissions
surface_id: module_content
required_permissions: [components.layout.view]Notes¶
- The host
surface_idmust match the content builder surface id. - Use one active host for a given content
surface_idin a shell. - Keep navigation and content surface concerns separate.
- Put scrolling or split behavior around
SurfaceHost, not inside the content surface by default. - Use
builder.set_surface(...)orprepare_shell_surface(...)for routes that render into the host.