Android UI does not have to be a binary choice between the traditional View system and a full Jetpack Compose migration.
ViewCompose explores a third path: describe UI with a state-driven Kotlin DSL, let a dedicated runtime handle composition and incremental updates, and still render a real Android View tree.
For years, discussions about Android UI have often been reduced to two options: continue with the mature but mostly imperative View system, or move to Jetpack Compose.
ViewCompose takes a different approach. Application code describes the interface declaratively. The framework handles state observation, composition, identity, diffing, patching, effects, and transactional commits. The final output, however, is still made of native TextView, EditText, RecyclerView, ViewGroup, and other Android Views.
That distinction matters. It means teams can adopt a modern state-driven development model while continuing to use AndroidX, existing View-based screens, custom Views, and third-party SDKs that were designed around the native View ecosystem.
ViewCompose is not a Jetpack Compose compatibility layer, and it does not reimplement the Compose Compiler. It is a separate declarative runtime designed specifically around the behavior and constraints of Android Views.
In one sentence: ViewCompose separates how UI is described from what ultimately renders it: the upper layers are declarative and composable, while the bottom layer remains the native Android View system.
The problem is larger than Kotlin syntax
Wrapping new View, addView, and setText in a few Kotlin functions does not create a declarative framework. The application would still be responsible for:
- synchronizing state with Views;
- organizing partial refreshes;
- preserving node identity and reuse;
- coordinating lifecycle and saved state;
- managing asynchronous effects;
- recovering when an update fails halfway through.
ViewCompose moves these cross-cutting responsibilities into the runtime:
- State reads establish dependencies. Changes are coalesced and applied at frame boundaries.
-
VNodeandNodeSpecrepresent component semantics as an immutable tree. Stable keys, content types, and structural identity participate in reconciliation. - The renderer creates, binds, moves, reuses, resets, and patches nodes according to differences instead of rebuilding the entire View tree.
- Native-tree updates have prepare, rollback, and commit boundaries. Irreversible work is explicitly deferred to
onCommit. - State, effects, saveable state, lifecycle, ViewModels, and host sessions have defined ownership.
The important improvement over a conventional View screen is therefore not merely “syntax that looks like Compose.” It is the consolidation of UI infrastructure that would otherwise be scattered across Activities, Fragments, custom Views, adapters, and utility classes.
Five architectural layers
The runtime is organized into five layers: Kernel, UI Foundation, Android Engine, Design System, and Integrations. Build-time checks prevent upward dependencies, Material concepts leaking into neutral layers, AndroidX entering the pure Kotlin core, and packages drifting into the wrong owner.
The aggregate artifacts viewcompose-android and viewcompose-material3-android are application entry points rather than additional architectural layers. Preview and benchmark tooling remain orthogonal to the runtime.
1. Kernel: stable rules in pure Kotlin
State observation, text editing, UI contracts, navigation transactions, animation engines, gesture policy, and graphics models remain pure Kotlin/JVM wherever possible.
This reduces Android coupling and makes the rules independently testable on the JVM. For example, TextFieldState owns text, selection, IME composition, transformations, undo, and redo. The navigation core owns routes, back stacks, and two-phase transactions. Platform layers only adapt those contracts to Android.
2. UI Foundation: a public DSL without renderer or brand coupling
Layouts, components, Theme/Defaults, UiLocal, composition coordination, animations, gestures, and drawing DSLs sit above the renderer. They express what the application needs instead of directly mutating an Android View.
The boundaries are deliberate:
-
Modifierowns reusable decoration and parent layout data. -
NodeSpecowns component semantics. - Theme/Defaults own design defaults.
Keeping these responsibilities separate prevents every capability from eventually becoming one universal parameter bag.
3. Android Engine: turning semantic nodes into native Views
The Renderer owns node factories, binders, diff plans, container reuse, View modifier application, and patches. The Host owns renderInto, RenderSession, platform service installation, frame scheduling, native View interoperability, and final disposal.
Neither layer owns Material policy, and neither pushes application-level DSL concepts back into the platform core.
4. Design Systems: supported, but not foundational
Material 3 and One UI 7 are independent design-system modules. Material is a first-class option, but it is not the foundation of the runtime.
Design policies are resolved into neutral color, geometry, motion, semantic, and fallback contracts before the Android Engine executes them. The renderer does not need a brand-specific branch.
This allows the same runtime, state model, layout vocabulary, interaction foundation, and renderer to support different design systems while leaving truly different component structures to their proper owners.
5. Integrations: optional capabilities stay optional
Overlay, Coil, Glide, Paging, CameraX, Google Maps, Media3, ConstraintLayout, AndroidX lifecycle, and ViewModel support live in the modules that own those dependencies.
An application can use an aggregate dependency for convenience, while infrastructure teams can select only the low-level contracts or integrations they actually require.
What ViewCompose changes for existing View applications
From imperative updates to state-driven UI
A traditional View screen often combines XML, ViewBinding, adapters, listeners, Flow collection, and a growing set of updateXxx functions. As the screen becomes interactive, “who updates which View, and when?” becomes a major source of complexity.
With ViewCompose, UI is derived from state. A state change invalidates the scopes that observed it, and the runtime coalesces work before updating the required nodes.
From isolated refresh mechanisms to one diff/patch/reuse model
Android developers are familiar with DiffUtil in RecyclerView, but ordinary screens, nested containers, overlays, and third-party Views often use completely different update strategies.
ViewCompose applies stable identity, node differences, payload patches, reset rules, and permanent release through one reconciliation model. Lazy containers and pagers also have their own session-refresh paths so that content can update even when the surrounding container structure remains stable.
From partially applied mutations to transactional tree updates
A complex UI update may create Views, move nodes, mutate properties, and bind external resources in a single frame.
ViewCompose distinguishes replayable update/reset operations, onCommit work that runs only after the complete tree succeeds, and one-time onRelease work when a node is permanently abandoned. A failed frame can restore the last committed tree instead of leaving a partially updated UI behind.
Native interoperability is the default, not an escape hatch
The final nodes are native Views or third-party Views hosted through AndroidViewAdapter.
For applications that depend on IME behavior, accessibility, focus, hardware keys, nested scrolling, maps, cameras, media playback, or OEM-specific behavior, this path reuses the platform's established implementation and supports gradual adoption inside an existing View hierarchy.
ViewCompose and Jetpack Compose are built on different foundations
ViewCompose borrows declarative concepts such as state, remember, effects, modifiers, lazy containers, and themes. It does not pretend to be Compose.
The two frameworks differ in compilation, layout authority, invalidation, interoperability, and host ownership:
| Dimension | Traditional Views | Jetpack Compose | ViewCompose |
|---|---|---|---|
| Programming model | Primarily imperative | Declarative | Declarative |
| Final render tree | Android Views | Compose UI nodes | Android Views |
| Compiler dependency | No Compose Compiler | Requires Compose Compiler plugin | No Compose Compiler |
| Layout authority |
MeasureSpec / LayoutParams
|
Compose Constraints |
MeasureSpec / LayoutParams
|
| Incremental updates | Usually organized by application code | Compiler and runtime cooperate | Explicit groups, state observation, diff/patch |
| Native interop | Direct | Bridges such as AndroidView
|
Native tree plus AndroidViewAdapter
|
| Design system | Assembled by the application | Mature Material ecosystem | Optional Material, neutral engine, multiple design systems |
| Maturity | Platform-level maturity | Mature ecosystem and tooling | Alpha; broad scope, still converging |
This comparison is about technical positioning, not a performance ranking.
A native View tree is the most visible difference
Compose operates through its own UI nodes, measurement, drawing, and semantics systems. ViewCompose maps declarative output to Android Views.
That makes existing View themes, accessibility, IME behavior, window insets, focus chains, RecyclerView reuse, and third-party controls closer to their original engineering boundaries. It also avoids adding the Compose rendering engine solely to gain declarative syntax.
None of this proves that ViewCompose is universally faster, smaller, or quicker to start than Compose. Those claims require measurements on the same device, build mode, and workload.
No Compose Compiler means more explicit boundaries
Compose uses compiler-generated restart groups, stability inference, and skipping logic. ViewCompose uses ComposerLite, SlotTable, state-read observation, and explicit node groups for incremental composition.
This makes the mechanism traceable without a Compose Compiler plugin. The tradeoff is equally important: ViewCompose does not have Compose's automatic stability inference or strong skipping. Developers must keep state reads inside sufficiently small and stable component boundaries.
Android View measurement remains authoritative
ViewCompose Row, Column, and Box containers ultimately measure and place native ViewGroup children. Width, height, padding, margin, and fill policies resolve to LayoutParams or container rules.
Compose Measurable, Placeable, and custom Layout implementations cannot be copied directly. Screens that rely on custom Compose measurement must use the built-in containers, ConstraintLayout integration, or a custom ViewGroup.
For View-oriented teams this avoids maintaining two competing layout models. It also makes migration more honest: semantic differences are documented rather than hidden behind similarly named APIs.
Modifier is not a copy of Modifier.Node
ViewCompose Modifier is an immutable value chain that the renderer folds by property family and maps onto native Views. Component semantics belong to NodeSpec, parent layout data belongs to scoped modifiers, and design defaults belong to Theme/Defaults.
Application-defined Modifier.Node lifecycles are not currently supported. This gives up some of Compose's extension freedom in exchange for a clearer renderer boundary and more predictable View reuse.
The design system is not the engine's default policy
Compose and Material have an exceptionally mature relationship. ViewCompose differs by preventing Material from entering Kernel, UI Foundation, or Android Engine.
Material 3 is a named module. One UI 7 is another independent module. Products can build their own design system. The Renderer consumes resolved neutral contracts without knowing which brand produced them.
Thirty-eight public artifacts instead of one monolith
At the documentation baseline used for this article, the public module catalog contains 38 Maven artifacts, each with an owning module manual.
- Kernel — 7: runtime, text core, UI contracts, navigation core, animation core, gesture core, and graphics core.
- UI Foundation — 4: declarative UI foundation plus animation, gesture, and graphics DSLs.
- Android Engine — 2: the native View renderer and host/session layer.
- Design Systems — 2: Material 3 and the One UI 7 Alpha design language.
- Integrations — 16: diagnostics, navigation, overlays, images, lifecycle, ViewModel, shadows, ConstraintLayout, media, maps, camera, and paging.
- Aggregates — 2: neutral and Material-oriented application entry points.
- Preview Tooling — 5: preview protocol, discovery, isolated rendering, worker host, and IDE-facing preview support.
Benchmark infrastructure is maintained as an orthogonal engineering tool rather than an application runtime dependency.
Documentation and comments are part of the product
One of ViewCompose's strongest engineering choices is treating documentation, comments, samples, and quality gates as framework deliverables.
The documentation separates architecture, tutorials, guides, Compose migration, module manuals, tooling, performance, roadmap, and release processes. Every public artifact has an owning manual, and active English documentation pages have Simplified Chinese mirrors.
The public API documentation policy includes several enforceable rules:
- Public and protected APIs use Q0–Q3 quality levels. Ordinary public APIs must meet a contract-complete Q2 standard; high-risk APIs require Q3.
- KDoc/Javadoc covers applicable ownership, lifecycle, concurrency, callback, failure, Android behavior, performance, and compatibility contracts rather than merely repeating a symbol name.
- Q3 APIs reference compiled
@samplefunctions. - Documentation snippets and sample sources are checked for consistency.
- All public modules participate in strict API documentation checks, and Dokka warnings can fail the build.
- Durable implementation comments explain invariants, concurrency, rollback, or platform workarounds instead of narrating code line by line.
- Architecture dependencies, design-system isolation, documentation structure, translation mirrors, module ownership, migration samples, and release changes have automated verification.
For adopters, this matters beyond onboarding. It makes questions such as “who owns this state?”, “how does failure recover?”, “when is this resource released?”, and “which artifact changes?” answerable over the lifetime of a large application.
Tooling beyond the runtime
The project also develops Android Studio static preview, source-to-rendered-result navigation, light/dark and device configurations, VNode/Composition/Patch/Recomposition diagnostics, isolated Layoutlib workers, screenshot regression, and Macrobenchmark infrastructure.
Diagnostics can expose the rendered tree, node-patch timeline, UiLocal snapshots, recomposition reasons, and optional bounded production failure aggregation.
Development tooling is intentionally isolated behind optional, explicitly activated paths. Applications do not need preview or benchmark dependencies at runtime, and inactive diagnostics cannot own recurring hot-path work.
Who is this for?
ViewCompose is most relevant to teams that:
- own substantial XML, custom View, RecyclerView, media, map, camera, or vendor-SDK investments;
- want gradual declarative adoption instead of a one-time rendering-stack rewrite;
- are sensitive to IME, focus, accessibility, hardware input, windows, and OEM behavior;
- need a product design system beyond Material, with a strict boundary between design policy and rendering;
- value readable runtime source, independent artifacts, contract documentation, compiled samples, architecture checks, and diagnostics.
If a team is starting a new application without View constraints, already knows Compose well, and relies heavily on its mature components, libraries, IDE support, and community knowledge, Jetpack Compose may remain the lower-risk default.
ViewCompose's value is clearest where declarative productivity and native View constraints exist at the same time.
Current limitations
A credible framework description also needs to explain what is not ready.
The project is Alpha
The project has published an initial public Alpha, so APIs and behavior can still change. Independent artifact versions reduce unrelated upgrade impact but also require more deliberate compatibility management.
Production adoption should be evaluated against real screens, devices, and dependencies—not only demos.
The ecosystem is much smaller than Compose
Compose has official resources, mature components, a broad third-party ecosystem, years of production experience, strong IDE integration, and a large hiring pool.
ViewCompose has its own previews, diagnostics, and migration documentation, but its community size, real-world case studies, searchable knowledge, and contributor base remain early.
Compose features are not one-to-one compatible
The current framework does not provide general Compose custom Layout APIs, application-defined Modifier.Node, direct AndroidViewBinding, or Fragment nodes inside the rendered tree.
UiLocal changes require observable state. Arbitrary subtree ViewModel scopes, some nested inset-consumption cases, some derived-state/snapshot semantics, and custom host restoration are narrower than their Compose equivalents. Migration requires semantic redesign rather than mechanical API renaming.
Device validation is still incomplete
The roadmap retains validation work for Chinese and Japanese IME behavior, TalkBack, Switch Access, hardware keyboards, multi-window, OEM themes, and dark/tablet screenshots on real devices.
One UI 7 remains an Alpha design-system subset. Additional Material TextField structure and Switch/Slider geometry and motion work are also candidates for product-driven convergence.
Native Views do not automatically mean better performance
The repository includes R8 release builds, Macrobenchmark, Compose comparison scenarios, memory metrics, diff/payload/SlotTable/subtree-skipping work, and shadow backend evaluation.
The project does not claim performance equivalence with Compose. The defensible claim is that ViewCompose has an explicit performance model and measurement infrastructure. Whether it is faster or smaller must be answered with comparable data. Baseline Profile gains also remain to be quantified.
It is an engineering experiment, not a production recommendation
ViewCompose is currently presented as an engineering experiment and is not production-ready. Much of the implementation has been built with AI assistance, while the repository attempts to make architectural, documentation, sample, and verification requirements explicit and enforceable.
That provenance should encourage scrutiny rather than lower it. Prospective users should inspect the source, run the verification suite, test on their own device matrix, and treat every adoption decision as an engineering evaluation.
A reconstruction of the View ecosystem—not a Compose clone
The most interesting part of ViewCompose is not the number of functions that resemble Compose APIs. It is the attempt to rebuild the central ideas of declarative UI—state-driven rendering, composition, incremental updates, stable identity, structured effects, and tooling—on top of native Android Views.
It respects View measurement, lifecycle, input, and ecosystem constraints while using strict layers to prevent platform details, design systems, and optional integrations from contaminating the core.
For teams maintaining large Android applications, that is a pragmatic direction: preserve existing assets, avoid a forced one-time rewrite, and still move future screens toward a declarative development model.
With 38 public artifacts, per-module manuals, strict KDoc/Javadoc, compiled samples, a Compose migration matrix, previews, diagnostics, and performance gates, ViewCompose is intended to be more than a UI DSL. It is an experiment in building a maintainable declarative framework for the reality of the Android View ecosystem.
Project thesis: preserve the determinism and compatibility of native Android Views while gaining the expression and engineering efficiency of declarative UI.
Learn more
This article reflects the repository README, architecture rules, design-system standard, module catalog, Compose migration matrix, API documentation policy, performance documentation, and roadmap at the time of writing. Capabilities and maturity may change as independently versioned modules evolve.

Top comments (0)