View Markup Language (VML) is a markup language derived from SGML, designed to represent composable UI frameworks for unidirectional, server-driven rendering. This specification defines the conversion from a server-side view model to VML for use with Jetpack Compose.
Specification Note 1.1: Scope The VML specification is concerned exclusively with the structure and rendering of composables. It is a unidirectional standard (server-to-client). Mechanisms for handling user interactions, actions, and client-to-server state changes are explicitly outside the scope of this document.
When serving VML documents for Jetpack Compose:
Request Headers:
ACCEPT: application/jetpack
Response Headers:
CONTENT-TYPE: application/jetpack
VML documents for Compose must begin with a doctype declaration.
<!doctype jetpack>
<vml>
<head>
<Style url="/styles/main.vss"/>
</head>
<body>
<Text>Hello, Compose!</Text>
</body>
</vml>
VML documents require <vml>
, <head>
, and <body>
tags.
<vml>
<head>
<Style url="/styles/main.vss"/>
</head>
<body>
<Column>
<Text>Hello, Compose!</Text>
</Column>
</body>
</vml>
The head section contains:
- Style references using the
<Style>
tag. - Template definitions for lifecycle views.
The <body>
tag is required and must contain the application's primary composable hierarchy.
Composables for different UI lifecycle states can be defined in the <head>
section using the template
attribute.
Specification Note 3.4: Template Invocation The VML specification defines the syntax for declaring lifecycle templates. The mechanism for triggering the rendering of these templates is the responsibility of the client-side implementation. The
template
attribute serves as a well-known identifier for the client to look up and render when its internal state changes.
Jetpack Compose functions convert directly to VML elements with exact case preservation.
// Jetpack Compose Code
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text("Hello")
Row {
Text("World")
}
}
Converts to:
<Column horizontalAlignment="Alignment.CenterHorizontally">
<Text>Hello</Text>
<Row>
<Text>World</Text>
</Row>
</Column>
Specification Note 4.1: Client-Side Composable Registry A VML client implementation must maintain a registry that maps VML element tag names to initializable Composable functions. The application developer is responsible for populating this registry at startup.
Composable function parameters become element attributes. The VML attribute name must match the Kotlin parameter name.
Specification Note 4.2: Type-Aware Deserialization & Data Types The client is responsible for performing type-aware deserialization by introspecting the function signature of the registered Composable. The following string representations must be supported:
Dp
/Sp
: Values can be plain integers (e.g.,padding="16"
, defaulting todp
) or have an explicit suffix ("16dp"
,"14sp"
).Color
: Values can be constant names ("Red"
) or hex strings ("#FFFF0000"
).- Enums / Static Objects: Values must use the full dot-qualified name (
"Alignment.CenterHorizontally"
).
All Jetpack Compose Modifiers are converted into a single, semi-colon-delimited style
attribute. The order of modifiers must be preserved.
// Jetpack Compose Code
Text(
"Hello",
modifier = Modifier.padding(16.dp).background(Color.Blue)
)
Converts to:
<Text style="padding(16dp); background(#FF0000FF)">Hello</Text>
Specification Note 4.3: Reserved
modifier
keyword The attribute namestyle
is used for modifier chains to maintain consistency. The attribute namemodifier
is reserved by the VML client and must not be used.
Composable lambda parameters are populated using the template system.
Default Content Slot: Child elements without a template
or slot
attribute fill the default content: @Composable () -> Unit
parameter.
Named Content Slots: To populate multiple named slots (e.g., in Scaffold
), the parent element uses attributes to reference templates defined on child elements.
// Jetpack Compose Code
Scaffold(
topBar = { TopAppBar(title = { Text("App") }) },
content = { LazyColumn() }
)
Converts to:
<Scaffold topBar=":myTopBar">
<TopAppBar template="myTopBar" title="App" />
<LazyColumn /> </Scaffold>
The attr
helper is used to create parameterized styles by referencing the value of another attribute on the same element. Its primary purpose is to allow a single style class from a stylesheet to be reused with dynamic data.
// Jetpack Compose Code
Column(modifier = Modifier.padding(paddingValue.dp))
Can be converted to VML as:
<Column paddingValue="16" style="padding(attr(:paddingValue))" />
Styles can be extracted into separate stylesheets and referenced using class
attributes.
<Button class="primary-button">Submit</Button>
Specification Note 6.1: Client-Specific Stylesheet Syntax The VML standard does not define the syntax for the content of stylesheet files. The syntax must be client-specific and consistent with the target native UI framework (e.g., Jetpack Compose
Modifier
syntax for an Android client). The rules of precedence between inlinestyle
attributes andclass
attributes are also an implementation detail of the client.
This section provides further examples of how different Jetpack Compose function signatures are converted to VML.
A standard composable with named parameters directly maps to attributes.
// Jetpack Compose Usage
Row(verticalAlignment = Alignment.CenterVertically) {
Text(text = "Enable Notifications")
Switch(checked = true, onCheckedChange = null)
}
Converts to:
<Row verticalAlignment="Alignment.CenterVertically">
<Text>Enable Notifications</Text>
<Switch checked="true" />
</Row>
Different function overloads for a composable can be disambiguated by the presence of unique attribute names. The client's Composable Registry is responsible for selecting the correct overload based on the provided attributes.
// Hypothetical function overloads
@Composable fun Image(painter: Painter, ...)
@Composable fun Image(url: String, ...)
// VML Client Registration
registry.register("Image", ::Image(painter:))
registry.register("Image", ::Image(url:))
The VML can then clearly select an overload:
<Image painter="my_local_icon" />
<Image url="https://example.com/image.png" />
- Elements must be properly nested and closed.
- Template names must be unique only among sibling elements.
- Templates must be defined as direct children of the element referencing them.
- All attribute values must be properly encoded.
- VML clients must implement a Composable Registry.
- The
modifier
attribute must not be used. - Named content slots must be populated using the attribute-to-template reference system.
- File extension:
.vml
- Encoding: UTF-8