Jetpack Compose for XR

Declaratively build spatial UI layouts that take advantage of Android XR’s spatial capabilities.
Latest Update Stable Release Release Candidate Beta Release Alpha Release
July 30, 2025 - - - 1.0.0-alpha05

Declaring dependencies

To add a dependency on XR compose, you must add the Google Maven repository to your project. Read Google's Maven repository for more information.

Add the dependencies for the artifacts you need in the build.gradle file for your app or module:

Groovy

dependencies {
    implementation "androidx.xr.compose:compose:1.0.0-alpha05"

    // Use to write unit tests
    testImplementation "androidx.xr.compose:compose-testing:1.0.0-alpha05"
}

Kotlin

dependencies {
    implementation("androidx.xr.compose:compose:1.0.0-alpha05")

    // Use to write unit tests
    testImplementation("androidx.xr.compose:compose-testing:1.0.0-alpha05")
}

For more information about dependencies, see Add build dependencies.

Feedback

Your feedback helps make Jetpack better. Let us know if you discover new issues or have ideas for improving this library. Please take a look at the existing issues in this library before you create a new one. You can add your vote to an existing issue by clicking the star button.

Create a new issue

See the Issue Tracker documentation for more information.

Version 1.0

Version 1.0.0-alpha05

July 30, 2025

androidx.xr.compose:compose:1.0.0-alpha05 and androidx.xr.compose:compose-testing:1.0.0-alpha05 are released. Version 1.0.0-alpha05 contains these commits.

New Features

  • Made SubspaceComposable annotation class public. (Ic2a34, b/399432430)
  • Two new SpatialExternalSurface Composables representing 180 and 360 degree spheres. (I40ef2, b/391705799)
  • Added SubspaceModifier.aspectRatio (Ide5ab, b/399729509, b/414762147)
  • Added the SceneCoreEntity API to improve interoperability between SceneCore and Compose for XR. (I50bb3, b/423020989)
  • Provided GravityAlignedsubspace API to support the unscaled And GravityAligned feature (I07359)

API Changes

  • SpatialDialog() will follow SpatialDialogProperties.dismissOnBack press configuration. (Ib453b, b/416797132)
  • Update minimumPanelDimension to a new default Dimension size of Dimensions(0.1f, 0.1f, 0.1f) due its representation in Meters. (Ib852a)
  • Subspaces and Orbiters will now retain their internal state in home space and when the app is in the background. In home space mode, Subspace will still set up its scene in preparation for the switch to full space mode. (I40317, b/416037751)
  • SpatialDialogs will now retain their state when the app is in the background. (I6aa56)
  • ApplicationSubspace will now inherit its recommended scale and position from the system. (I4565f, b/418834194)
  • Added a better error message and trigger the error earlier when a SubspaceComposable is used in a non-SubspaceComposable context. (Iee2ae, b/416484684)
  • Updating ExperimentalSubspaceVolumeApi from Warning to Error because warnings are often overlooked when misusing composable APIs. (I427aa, b/424864286)
  • Subspace and ApplicationSubspace are now constrained by recommendedContentBoxInFullSpace. Previously it was constrained by SpatialUser's Field of View. (I41015, b/423074142)
  • Update SpatialElevation to use min size to no longer use hard coded size (I2dbe6, b/427785338)
  • Update how we scrim the SpatialAcitivityPanel to update when a key variable is modified. (I0f64d, b/427999029)
  • Remove VolumeConstraints.Unbounded in favor of setting the default constraint values to the equivalent. (Ie24ec, b/407938414)
  • SpatialFeatheringSize is no longer public (I1c15b, b/399432430)
  • Renamed the XR Placeable to SubspacePlaceable to distinguish it from Compose's Placeable. (I74874)
  • Removing Orbiter settings and adding shouldRenderInNonSpatial as a new param. In addition, removing class EdgeOffset and adding orbiterOffsetType as a new param to consolidate Orbiter() Functions. As well as renaming OrbiterEdge to ContentEdge. (Iebf3d)
  • Renamed Measurable to SubspaceMeasurable to differentiate the type from Compose's Measurable type. (I9726c)
  • Rename MeasureResult to SubspaceMeasureResult (I9f34d)
  • Removed the setSubspaceContent API in favor of using Compose's setContent with a Subspace composable. (Ifff4c, b/421427391, b/421427391)
  • MeasurePolicy is renamed to SubspaceMeasurePolicy. (I37a9b, b/422553904)
  • Turn SubspaceSemanticsInfo into a sealed interface because we won't be able to add members without the defaults. (I372f9, b/423704068)
  • Updated SpatialExternalSurface documentation, renamed ContentSecurityLevel to SurfaceProtection (I3c460, b/420982808)
  • Provided overloaded constructor for movable modifier which allows anchoring. (Ic0c70)
  • Add more position provider for tooltips so now developers can control if the tooltip is placed above, below, left, or right of the anchor. Add an API that takes in a Shape for carets, so more custom shapes can be provided. (Ie513c, b/374766087, b/418854637)
  • Removed CoreEntity as a PublishedApi (Ifee05)

Bug Fixes

  • Fixed issue where SpatialDialog would flash when being rendered. (Ife73c, b/401619909)
  • Fixes issue where SpatialDialog could not scrim the Activity Panel. (I8ca6c, b/367442109)
  • Fix XR dialog not showing some content (I17cd5, b/418062437)
  • Fixed issue where SpatialPopup was being dismissed when clicked inside of the content. (If262c, b/417245722)
  • Fixed the issue where when chaining resizable().movable() the SpatialPanel failed to resize correctly to the new size. (I02ee3, b/422264230)
  • Fixed topBar overlapping with menu in SpatialComposeVideoPlayer (Id33bc, b/427168167)
  • Fixed corner radius not rendering (I975fe, b/428261830)

Version 1.0.0-alpha04

May 7, 2025

androidx.xr.compose:compose:1.0.0-alpha04 and androidx.xr.compose:compose-testing:1.0.0-alpha04 are released. Version 1.0.0-alpha04 contains these commits.

New Features

  • Added CompositionLocalConsumerSubspaceModifierNode interface to allow custom SubspaceModifier types to access composition local values.
  • Added a new SpatialPanel API that follows the compose AndroidView implementation style and deprecates the previous ViewBased SpatialPanel.
  • Added VolumeConstraints.Unbounded companion object which represents unbounded constraints.
  • Added SubspaceModifier.onPointSourceParams to allow a spatialized audio source.
  • A public ApplicationSubspace has been added, offering optional VolumeConstraints to define a 3D area where the app can render spatial content. By default, if no constraints are specified, the Subspace will be bounded by the SpatialUser's current field of view in width and height. Users can provide constraints to be used if the field of view cannot be determined. Otherwise, the default field of view width and height values are used.
  • Added SpatialExternalSurface, which can be used to render stereoscopic content. SpatialExternalSurface is customizable with modifiers (except alpha), and an edge feathering effect.
  • Added a new pointerHoverIcon Subspace Modifier that allows users to set the icon for the spatial pointer.

API Changes

  • Removed RequiresApi(34) restriction on all Jetpack XR packages. This restriction was redundant as Jetpack XR is currently only available on devices with API level 34+. (Iae0f8)
  • Projects released with Kotlin 2.0 require KGP 2.0.0 or newer to be consumed. (Idb6b5)
  • Back handling will now work on spatial panels without embedded activities. For back handling to work you need to specify android:enableOnBackInvokedCallback="true" in the android manifest.
  • Backhandling will now work on spatial dialogs. For backhandling to work you need to specify android:enableOnBackInvokedCallback="true" in the android manifest.
  • Compose-based and View-based SpatialPanels can now size themselves based on their contents.
  • Developers may now set their own custom SpatialElevationLevel values and are not limited to the predefined levels.
  • Orbiter elevation level may now be customized via the elevation parameter.
  • Subspace can now be bounded by the SpatialUser's field of view in width and height by default. If the field of view cannot be determined, the default field of view width and height values are used.
  • Added new callbacks onMoveStart and onMoveEnd to the Movable modifier. The onMoveStart and onMoveEnd callbacks are called when the user starts and ends moving a subspace composable with the movable modifier.
  • The name parameter has been removed from spatial APIs such as SpatialRow and SpatialPanel. For debugging spatial compose trees use SubspaceModifier.testTag instead.
  • Removed an unsupported overload of SpatialPopup that only has spatialElevationLevel and content. Please use the interface that supports onDimissRequest.
  • The onPoseChange callback from the Movable modifier has been removed. Use onMove instead.
  • SubspaceModifiers will no longer apply their effects if they are detached or currently detaching.
  • The existing SpatialRow API has been split into SpatialRow and SpatialCurvedRow. If previously using SpatialRow's curveRadius parameter, use SpatialCurvedRow now instead which offers the same behavior.
  • MainPanel and ActivityPanel no longer have title bars when run on a similarly recent system image.
  • Alpha and scale modifiers are now stackable and will multiply their values for the final applied alpha or scale value.
  • The onPoseChange callback from the Movable modifier has been optimized to perform smoother pose movement.
  • The movable and resizable modifiers will now perform their callbacks on the main thread to ensure that state changes will trigger recomposition.
  • Added state observation to the layout and measure phases to ensure that state changes in SubspaceLayout will trigger relayout.
  • Optimized modifier chain updates to better reuse existing modifiers.

Bug Fixes

  • Stopped scrimming when a SpatialDialog is shown. (Ic4594)
  • Relayout requests made while modifier nodes are detached will now be ignored.
  • Removed relayout phases triggered by Movable and Resizable modifiers.
  • Fixed a crash in MainPanel() composable that occurred when either dimension was set to zero, either directly or during a layout calculation, e.g., a SpatialRow/SpatialColumn calculation. The panel will now be hidden instead. Note that this fix specifically addresses crashes during the layout phase; resizing the panel to zero via user interaction will be handled separately. The hidden panel lacks UI affordances.
  • Fixed issue with maintainAspectRatio from the resizable modifier. The aspect ratio should be kept now.
  • Fixed an issue with nested Subspaces where they would be incorrectly positioned for a single frame.
  • Fixed issue where rounded corners were sometimes not applied when they should be.
  • NestedSubspaces will no longer appear for one frame in the wrong location.

Version 1.0.0-alpha03

February 26, 2025

androidx.xr.compose:compose:1.0.0-alpha03 and androidx.xr.compose:compose-testing:1.0.0-alpha03 are released with no notable changes since the last alpha. Version 1.0.0-alpha03 contains these commits

Version 1.0.0-alpha02

February 12, 2025

androidx.xr.compose:compose:1.0.0-alpha02 and androidx.xr.compose:compose-testing:1.0.0-alpha02 are released. Version 1.0.0-alpha02 contains these commits.

New Features

  • The Activity Panel can now scrim its content when a Spatial Dialog is activated.
  • The Orbiter API is now usable in SubspaceComposable contexts and will attach Orbiters to their nearest SubspaceLayout-based composable parent.
  • Introduced LayoutCoordinatesAwareModifierNode to allow custom positioning-based modifiers.
  • Added attach/detach lifecycle methods to SubspaceModifier.Node.
  • Added scaleWithDistance to the movable modifier. When scaleWithDistance is enabled, the subspace element moved will grow or shrink. It will also maintain any explicit scale that it had before movement.

API Changes

  • Removed SessionCallbackProvider in favor of SpatialCapabilities.

Other changes

  • Reduced minSDK to 24. All Jetpack XR APIs continue to require API 34 at runtime.
  • Orbiter EdgeOffset.inner, EdgeOffset.outer, and EdgeOffset.overlap constructors are no longer @Composable methods, which allows them to be used in non-composable contexts.
  • Update Spatial Elevation Levels to match the latest UX spec.
  • Implement SubspaceSemanticsInfo interface into MeasurableLayout.
  • Renamed SubspaceModifierElement to SubspaceModifierNodeElement.

Bug fixes

  • Fixes to stabilize SubspaceModifier ordering. SubspaceModifier should behave more reliably. Offset, rotate, scale, movable, and resizable modifier should now be usable in any order.

Version 1.0.0-alpha01

December 12, 2024

androidx.xr.compose:compose-*1.0.0-alpha01 is released.

Features of Initial Release

  • Initial developer release of Jetpack Compose for XR. Use familiar Compose concepts such as rows and columns to create spatial UI layouts in XR, whether you're porting an existing 2D app to XR or creating a new XR app from scratch. This library provides subspace and spatial composables: such as spatial panels and orbiters, which let you place your existing 2D Compose or Views-based UI in a spatial layout. It introduces the Volume subspace composable, which allows you to place SceneCore entities, such as 3D models, relative to your UI. Learn more in this developer guide:

    • Subspace: This composable can be placed anywhere within your app’s UI hierarchy, allowing you to maintain layouts for 2D and spatial UI without losing context between files. This makes it easier to share things like existing app architecture between XR and other form factors without needing to hoist state through your whole UI tree or re-architect your app.

    • SpatialPanel: A spatial panel is a subspace composable that lets you display app content–for example, you could display video playback, still images, or any other content in a spatial panel.

    • Orbiter: An orbiter is a spatial UI component. It's designed to be attached to a corresponding spatial panel, and contains navigation and contextual action items related to that spatial panel. For example, if you've created a spatial panel to display video content, you could add video playback controls inside an orbiter.

    • Volume: Place SceneCore entities, such as 3D models, relative to your UI.

  • Spatial Layout: You can create multiple spatial panels and place them within a Spatial Layout using SpatialRow, SpatialColumn, SpatialBox, and SpatialLayoutSpacer. Use SubspaceModifiers to customize your layout.

  • Spatial UI components: These elements can be reused in your 2D UI, and their spatial attributes will only be visible when spatial capabilities are enabled.

    • SpatialDialog: Panel will push slightly back in z-depth to display an elevated dialog.
    • SpatialPopUp: Panel will push slightly back in z-depth to display an elevated popup
    • SpatialElevation: SpatialElevationLevel can be set to add elevation.
  • SpatialCapabilities: Spatial capabilities can change as users interact with your app or the system, or can even be changed by your app itself—for example, moving into Home Space or Full Space. To avoid issues, your app needs to check for LocalSpatialCapabilities.current to determine which APIs are supported in the current environment. isSpatialUiEnabled: Spatial UI elements (e.g. SpatialPanel) isContent3dEnabled: 3D objects isAppEnvironmentEnabled: The environment isPassthroughControlEnabled: Whether or not the application can control the passthrough state isSpatialAudioEnabled: Spatial audio

Known Issues

  • Currently a minSDK of 30 is required to use Jetpack Compose for XR. As a workaround you may add the following manifest entry <uses-sdk tools:overrideLibrary="androidx.xr.scenecore, androidx.xr.compose"/> to be able to build and run with a minSDK of 23.
  • Jetpack XR apps currently require requesting android.permission.SCENE_UNDERSTANDING permission in the AndroidManifest.
  • When an app launches directly into Full Space using the PROPERTY_XR_ACTIVITY_START_MODE property in their manifest, Activities/Applications are initially opened in Home Space before transitioning into Full Space.
  • glTFs in Volume Composables may initially flicker at the wrong location.
  • Using a SpatialDialog in a panel that has been moved significantly will push the content in the wrong direction.