
Share UI code across native and semantic HTML, with adaptive sizing, pane layouts, replaceable design systems, real HTML semantics and optional Material components for responsive, themeable apps.
Build shared Compose UIs for Android, iOS and the web. Render real HTML in the browser.
Documentation · Shopping demo · Playground · API reference
Domposable is a Kotlin Multiplatform UI library. Write your components and layouts in commonMain, then render them with Compose UI on Android and iOS or semantic HTML on Kotlin/JS. Desktop and Wasm are available as experimental targets.
Start with the included Material 3 controls, adapt them to your brand, and wrap them behind your own component names. Your app keeps its state management, dependency injection and navigation.
Domposable is used in Podiumas, a live sports tournament app. The examples below show smaller features you can explore and reuse.
Use components directly with Compose state, an existing ViewModel, Metro, Koin or another DI container. You do not need application KSP setup or a screen registry to use the components. Existing frameworks can also reuse the generator with their own annotations and hosts.
The core uses stable Compose Multiplatform. Native controls currently use Material 3 alpha APIs. See tested versions and platform boundaries.
[!NOTE] This README describes
main. Choose a published version from Releases. Before 1.0, minor releases may change APIs. Check the migration guide when upgrading.
Add mavenCentral() and google() to your repositories, then replace <released-version> with a version from Releases.
val domposableVersion = "<released-version>"
kotlin {
sourceSets {
commonMain.dependencies {
implementation("io.github.samuolis.domposable:domposable-ui:$domposableVersion")
// Optional: adaptive layout helpers.
implementation("io.github.samuolis.domposable:domposable-adaptive:$domposableVersion")
}
}
}The UI artifact includes the shared API and platform implementations. The Material artifact remains a compatibility alias. See the installation guide and tested toolchain for Compose plugins and target setup.
import androidx.compose.runtime.Composable
import io.github.samuolis.domposable.foundation.*
import io.github.samuolis.domposable.material.DomposableMaterialTheme
import io.github.samuolis.domposable.ui.*
@Composable
fun ContactsScreen(onAddContact: () -> Unit) {
DomposableColumn(
modifier = DomposableModifier.padding(16.dp),
gap = 12.dp,
) {
DomposableText("Contacts", style = DomposableTextStyle.Title)
DomposableButton("Add contact", onAddContact)
}
}
@Composable
fun App(onAddContact: () -> Unit) {
DomposableMaterialTheme {
ContactsScreen(onAddContact)
}
}Use Android setContent, iOS ComposeUIViewController, a desktop Compose window or Kotlin/JS renderComposable for HTML. The feature and its callbacks stay in commonMain. The host selects the platform's composition environment. An optional experimental Wasm host is available for Compose UI comparisons.
Platform entry-point examples →
For most apps, setup is simply DomposableMaterialTheme { AppContent() }. The theme supplies appearance values. Shared component calls use direct platform implementations to create controls and connect events to your callbacks.
flowchart TB
Feature[Shared feature composable] --> Components[Domposable component calls]
Components --> Platform[Compile-time expect / actual]
Platform --> Native[Compose UI: Android, iOS, desktop, Wasm]
Platform --> HTML[Semantic HTML and CSS: Kotlin/JS]Built-in components use expect / actual directly. There is no component provider
to configure. Keep your own names such as AppButton, wrap Domposable controls,
and set colors, typography, shapes and styles through the theme. For a different
control implementation, use an app-owned platform contract.
Follow the component call.
Set brand defaults once. Every argument is optional:
DomposableMaterialTheme(
colors = brandColors,
styles = DomposableComponentStyles(
button = DomposableButtonAppearance(shape = DomposableShape.Circle),
),
) {
FeatureScreen()
}Use DomposableTheme.styles.button.copy(...) for a local override. Nested themes inherit their enclosing brand values. Styles are composition-scoped, so different parts of an app can use different brands.
Custom cards are shared composition too: use DomposableBox for overlapping image/content layers, a portable gradient for the scrim, and the same theme for controls. Use application CSS when you need it inside a custom HTML renderer. Build a card without CSS · Card and pane demo.
Understand the API → · Customize a design system → · Build adaptive layouts →
| Your app | Adopt | Keep in your app |
|---|---|---|
| Starting a shared UI | Components, themes and adaptive helpers | Models, DI and application behaviour |
| Has a design system | Theme defaults and thin adapters behind existing component names | Brand tokens, product layouts and custom controls |
| Has Navigation 3 or another router | Optional entry display, pane and browser helpers | Back stack, entry decorators, route identity and URL policy |
| Has screen annotations and codegen | Optional KSP function driver | Annotation contract, DI bindings and generated host templates |
Components and adaptive helpers do not depend on the screen registry, navigator or ViewModel adapter. KSP is optional. Your app can use Koin, Metro, another DI container or no DI.
Existing Compose UI widgets cannot run inside an HTML composition. Sharing a feature requires portable component calls or your own native/HTML adapters. Existing Compose UI screens need migration beyond changing imports. Migrate one feature at a time →
| Target | Rendering | Support |
|---|---|---|
| Android, API 24+ | Compose UI + Material 3 | Supported |
| iOS arm64 and arm64 simulator | Compose UI + Material 3 | Supported |
| Web, Kotlin/JS | Semantic DOM + CSS + browser controls | Supported |
| JVM desktop | Compose UI + Material 3 | Experimental |
| Web, Kotlin/Wasm | Compose UI in the browser | Experimental |
HTML and Wasm support optional browser history, hash or path URLs and portable entry-state restoration. HTML collections render all keyed items by default. Columns, rows, grids and tables can opt into bounded windowing.
The HTML target is a client-rendered single-page application built on Compose HTML. It does not require Wasm and does not provide server-side rendering.
The optional public-page exporter captures HTML, metadata and generated CSS at build time for an explicit list of routes, including public event pages. These snapshots can display content before JavaScript starts. This is prerendering, not SSR, and it does not guarantee search-engine indexing. The exporter is available from version 0.22.0.
Compatibility, tested versions and platform boundaries →
drawBehind. Web adapters can also attach CSS and attributes directly.| Example | What to explore | Open |
|---|---|---|
| Fieldwork shop | Responsive storefront, product pages, filters, cart and checkout | Live app · Walkthrough |
| Documents and data | Rich inputs, typed choices, forms, disclosure, rich text, charts and interactive tables | Shared source · Recipes |
| Material and expressive catalog | Theme defaults, local appearance overrides, pickers, menus, expressive actions and screen chrome | Live catalog · Shared source · API guide |
| Component playground | Shared controls, theme tokens and renderer customization | Live app · Components |
| HTML / Wasm comparison | The same feature at phone, tablet and desktop widths | Compare |
| Browser routes | Clean paths, deep links and browser navigation | URL example · Routing guide |
| Large collections | Measured lists, horizontal rows, responsive grids and semantic tables | Gallery · Guide |
| Navigation policies | Route overlays, Back previews and live models across root switches | Recipe · Guide |
| Saved root stacks | Independent tabs, restored drafts and caller-owned Navigation 3 scenes | Recipe source · Guide |
The fictional Fieldwork shop shares its catalogue, cart and feature composables. On the web it uses real links and form controls, with product URLs such as /domposable/shop/products/canvas-weekender/.
See the Material catalog for standard and expressive variants, theme configuration and per-component overrides.
The extension example combines a custom label, native drawing and HTML/CSS decoration while keeping counter and input state in shared code.
The modular shopping example separates catalog and checkout into API and implementation modules. Metro injects entry-owned ViewModels, a composition local supplies typed navigation, and optional KSP generates feature entries. The screen guide also shows manual registration and direct Navigation 3 integration.
Use the pieces your application needs. All artifacts use io.github.samuolis.domposable:<artifact>:<released-version>.
| Artifact | Purpose |
|---|---|
domposable-foundation |
Portable values and theme tokens |
domposable-ui |
Shared components, platform implementations and ordered modifiers |
domposable-design-material |
Compatibility alias for domposable-ui
|
domposable-adaptive |
Content-sized breakpoints, preferences and pane recipes |
| Artifact | Purpose |
|---|---|
domposable-screens |
Typed registration and entry-owned resources/saveables |
domposable-navigation3 |
Navigation 3 hosts and HTML/Wasm browser history |
domposable-lifecycle-viewmodel |
AndroidX entry ownership and optional state/action helpers |
domposable-ksp |
Generated typed entries and feature entry lists |
| Artifact | Purpose |
|---|---|
domposable-export-gradle |
Public HTML/CSS snapshots from Kotlin Gradle and a route list (0.22.0+) |
| I want to… | Read |
|---|---|
| Install Domposable and set up platform hosts | Getting started |
| Understand components, props and platform implementations | How the API works |
| Use shared controls with examples | Components |
| Configure Material 3 and expressive components | Material catalog |
| Adapt a feature to its available space | Adaptive layouts |
| Build or customize a design system | Design systems |
| Add screens, Navigation 3 or generated entries | Screens and navigation |
| Export marketing and public event pages as HTML | Public-page export |
| Integrate Koin, Metro or Kobweb | Integrations |
| Follow the API/implementation module example | Modular app walkthrough |
| Check supported versions and limitations | Compatibility |
| Compare Wasm input and accessibility | Shared input lab and tested boundaries |
| Browse generated declarations | API reference |
Bug reports and focused contributions are welcome. Start with CONTRIBUTING.md for local setup, validation and maintainer workflows, or open an issue.
PR checks cover ktlint, detekt, Android lint, JVM/Android coverage floors, required binary API checks, packaged consumers, and functional and screenshot tests for Android, desktop, HTML and Wasm. iOS has compilation, ABI and packaged-consumer checks. Simulator runtime checks are not automatic release gates. CI keeps reports and captures in GitHub Actions artifacts. API baseline changes need compatibility, migration and version-impact notes.
Apache License 2.0. Copyright 2026 Samuolis.
Build shared Compose UIs for Android, iOS and the web. Render real HTML in the browser.
Documentation · Shopping demo · Playground · API reference
Domposable is a Kotlin Multiplatform UI library. Write your components and layouts in commonMain, then render them with Compose UI on Android and iOS or semantic HTML on Kotlin/JS. Desktop and Wasm are available as experimental targets.
Start with the included Material 3 controls, adapt them to your brand, and wrap them behind your own component names. Your app keeps its state management, dependency injection and navigation.
Domposable is used in Podiumas, a live sports tournament app. The examples below show smaller features you can explore and reuse.
Use components directly with Compose state, an existing ViewModel, Metro, Koin or another DI container. You do not need application KSP setup or a screen registry to use the components. Existing frameworks can also reuse the generator with their own annotations and hosts.
The core uses stable Compose Multiplatform. Native controls currently use Material 3 alpha APIs. See tested versions and platform boundaries.
[!NOTE] This README describes
main. Choose a published version from Releases. Before 1.0, minor releases may change APIs. Check the migration guide when upgrading.
Add mavenCentral() and google() to your repositories, then replace <released-version> with a version from Releases.
val domposableVersion = "<released-version>"
kotlin {
sourceSets {
commonMain.dependencies {
implementation("io.github.samuolis.domposable:domposable-ui:$domposableVersion")
// Optional: adaptive layout helpers.
implementation("io.github.samuolis.domposable:domposable-adaptive:$domposableVersion")
}
}
}The UI artifact includes the shared API and platform implementations. The Material artifact remains a compatibility alias. See the installation guide and tested toolchain for Compose plugins and target setup.
import androidx.compose.runtime.Composable
import io.github.samuolis.domposable.foundation.*
import io.github.samuolis.domposable.material.DomposableMaterialTheme
import io.github.samuolis.domposable.ui.*
@Composable
fun ContactsScreen(onAddContact: () -> Unit) {
DomposableColumn(
modifier = DomposableModifier.padding(16.dp),
gap = 12.dp,
) {
DomposableText("Contacts", style = DomposableTextStyle.Title)
DomposableButton("Add contact", onAddContact)
}
}
@Composable
fun App(onAddContact: () -> Unit) {
DomposableMaterialTheme {
ContactsScreen(onAddContact)
}
}Use Android setContent, iOS ComposeUIViewController, a desktop Compose window or Kotlin/JS renderComposable for HTML. The feature and its callbacks stay in commonMain. The host selects the platform's composition environment. An optional experimental Wasm host is available for Compose UI comparisons.
Platform entry-point examples →
For most apps, setup is simply DomposableMaterialTheme { AppContent() }. The theme supplies appearance values. Shared component calls use direct platform implementations to create controls and connect events to your callbacks.
flowchart TB
Feature[Shared feature composable] --> Components[Domposable component calls]
Components --> Platform[Compile-time expect / actual]
Platform --> Native[Compose UI: Android, iOS, desktop, Wasm]
Platform --> HTML[Semantic HTML and CSS: Kotlin/JS]Built-in components use expect / actual directly. There is no component provider
to configure. Keep your own names such as AppButton, wrap Domposable controls,
and set colors, typography, shapes and styles through the theme. For a different
control implementation, use an app-owned platform contract.
Follow the component call.
Set brand defaults once. Every argument is optional:
DomposableMaterialTheme(
colors = brandColors,
styles = DomposableComponentStyles(
button = DomposableButtonAppearance(shape = DomposableShape.Circle),
),
) {
FeatureScreen()
}Use DomposableTheme.styles.button.copy(...) for a local override. Nested themes inherit their enclosing brand values. Styles are composition-scoped, so different parts of an app can use different brands.
Custom cards are shared composition too: use DomposableBox for overlapping image/content layers, a portable gradient for the scrim, and the same theme for controls. Use application CSS when you need it inside a custom HTML renderer. Build a card without CSS · Card and pane demo.
Understand the API → · Customize a design system → · Build adaptive layouts →
| Your app | Adopt | Keep in your app |
|---|---|---|
| Starting a shared UI | Components, themes and adaptive helpers | Models, DI and application behaviour |
| Has a design system | Theme defaults and thin adapters behind existing component names | Brand tokens, product layouts and custom controls |
| Has Navigation 3 or another router | Optional entry display, pane and browser helpers | Back stack, entry decorators, route identity and URL policy |
| Has screen annotations and codegen | Optional KSP function driver | Annotation contract, DI bindings and generated host templates |
Components and adaptive helpers do not depend on the screen registry, navigator or ViewModel adapter. KSP is optional. Your app can use Koin, Metro, another DI container or no DI.
Existing Compose UI widgets cannot run inside an HTML composition. Sharing a feature requires portable component calls or your own native/HTML adapters. Existing Compose UI screens need migration beyond changing imports. Migrate one feature at a time →
| Target | Rendering | Support |
|---|---|---|
| Android, API 24+ | Compose UI + Material 3 | Supported |
| iOS arm64 and arm64 simulator | Compose UI + Material 3 | Supported |
| Web, Kotlin/JS | Semantic DOM + CSS + browser controls | Supported |
| JVM desktop | Compose UI + Material 3 | Experimental |
| Web, Kotlin/Wasm | Compose UI in the browser | Experimental |
HTML and Wasm support optional browser history, hash or path URLs and portable entry-state restoration. HTML collections render all keyed items by default. Columns, rows, grids and tables can opt into bounded windowing.
The HTML target is a client-rendered single-page application built on Compose HTML. It does not require Wasm and does not provide server-side rendering.
The optional public-page exporter captures HTML, metadata and generated CSS at build time for an explicit list of routes, including public event pages. These snapshots can display content before JavaScript starts. This is prerendering, not SSR, and it does not guarantee search-engine indexing. The exporter is available from version 0.22.0.
Compatibility, tested versions and platform boundaries →
drawBehind. Web adapters can also attach CSS and attributes directly.| Example | What to explore | Open |
|---|---|---|
| Fieldwork shop | Responsive storefront, product pages, filters, cart and checkout | Live app · Walkthrough |
| Documents and data | Rich inputs, typed choices, forms, disclosure, rich text, charts and interactive tables | Shared source · Recipes |
| Material and expressive catalog | Theme defaults, local appearance overrides, pickers, menus, expressive actions and screen chrome | Live catalog · Shared source · API guide |
| Component playground | Shared controls, theme tokens and renderer customization | Live app · Components |
| HTML / Wasm comparison | The same feature at phone, tablet and desktop widths | Compare |
| Browser routes | Clean paths, deep links and browser navigation | URL example · Routing guide |
| Large collections | Measured lists, horizontal rows, responsive grids and semantic tables | Gallery · Guide |
| Navigation policies | Route overlays, Back previews and live models across root switches | Recipe · Guide |
| Saved root stacks | Independent tabs, restored drafts and caller-owned Navigation 3 scenes | Recipe source · Guide |
The fictional Fieldwork shop shares its catalogue, cart and feature composables. On the web it uses real links and form controls, with product URLs such as /domposable/shop/products/canvas-weekender/.
See the Material catalog for standard and expressive variants, theme configuration and per-component overrides.
The extension example combines a custom label, native drawing and HTML/CSS decoration while keeping counter and input state in shared code.
The modular shopping example separates catalog and checkout into API and implementation modules. Metro injects entry-owned ViewModels, a composition local supplies typed navigation, and optional KSP generates feature entries. The screen guide also shows manual registration and direct Navigation 3 integration.
Use the pieces your application needs. All artifacts use io.github.samuolis.domposable:<artifact>:<released-version>.
| Artifact | Purpose |
|---|---|
domposable-foundation |
Portable values and theme tokens |
domposable-ui |
Shared components, platform implementations and ordered modifiers |
domposable-design-material |
Compatibility alias for domposable-ui
|
domposable-adaptive |
Content-sized breakpoints, preferences and pane recipes |
| Artifact | Purpose |
|---|---|
domposable-screens |
Typed registration and entry-owned resources/saveables |
domposable-navigation3 |
Navigation 3 hosts and HTML/Wasm browser history |
domposable-lifecycle-viewmodel |
AndroidX entry ownership and optional state/action helpers |
domposable-ksp |
Generated typed entries and feature entry lists |
| Artifact | Purpose |
|---|---|
domposable-export-gradle |
Public HTML/CSS snapshots from Kotlin Gradle and a route list (0.22.0+) |
| I want to… | Read |
|---|---|
| Install Domposable and set up platform hosts | Getting started |
| Understand components, props and platform implementations | How the API works |
| Use shared controls with examples | Components |
| Configure Material 3 and expressive components | Material catalog |
| Adapt a feature to its available space | Adaptive layouts |
| Build or customize a design system | Design systems |
| Add screens, Navigation 3 or generated entries | Screens and navigation |
| Export marketing and public event pages as HTML | Public-page export |
| Integrate Koin, Metro or Kobweb | Integrations |
| Follow the API/implementation module example | Modular app walkthrough |
| Check supported versions and limitations | Compatibility |
| Compare Wasm input and accessibility | Shared input lab and tested boundaries |
| Browse generated declarations | API reference |
Bug reports and focused contributions are welcome. Start with CONTRIBUTING.md for local setup, validation and maintainer workflows, or open an issue.
PR checks cover ktlint, detekt, Android lint, JVM/Android coverage floors, required binary API checks, packaged consumers, and functional and screenshot tests for Android, desktop, HTML and Wasm. iOS has compilation, ABI and packaged-consumer checks. Simulator runtime checks are not automatic release gates. CI keeps reports and captures in GitHub Actions artifacts. API baseline changes need compatibility, migration and version-impact notes.
Apache License 2.0. Copyright 2026 Samuolis.