The Jetpack Compose Grid API is designed for structural, two-dimensional screen
layouts. Unlike lazy grids, which are optimized for large data sets, Grid lets
you define a fixed or responsive layout structure using tracks (rows and columns),
gaps, and grid areas. You place items into this structure using Modifier.gridItem,
which gives you explicit control over positioning and spanning within the grid
container.
When your UI requires multiple items in the same grid area or overlapping
areas—such as badge overlays, status indicators, or layered cards—coordinate
ambiguity can make layout behavior unpredictable. Managing the depth of overlapping
components without resorting to complex Box nesting or manual coordinate offsets is
essential for maintaining clean, readable layout code.
Best practices
- Declaration order:
Gridfollows standardzIndexordering based on the order of declaration within theGridblock. Items declared later in theGridcontent block are rendered on top of items declared earlier. - Explicit z-index control: Use
Modifier.zIndexif you need to explicitly control or override the default drawing order of overlapping elements. - Named area vs item overlays: Use
Gridand named areas to define the structure of your layout, but keep complex overlay logic inside the specificgridItemif those overlays are logically tied to that grid cell. - Intentional spans for overlays: When overlaying, ensure that the
rowSpanandcolumnSpanparameters are intentional for the overlapping elements to control how much of the underlying content is obscured.
Ingredients
Grid: A structural, two-dimensional layout containerGridConfigurationScope.area: Scope function used to define named regions and overlay areas across grid tracksModifier.gridItem: Modifier used to place and span composables across grid tracks or into named grid areasModifier.zIndex: Modifier used to explicitly control drawing and layer elevation order
Steps
Configure your grid layout and named areas, position the base layer content within target grid coordinates, and place overlay elements in the shared grid cells or overlapping areas.
1. Define the grid configuration
Create a Grid composable configuration specifying row and column tracks, as well as named areas that span across cells to define overlay regions:
enum class Areas { BASE, OVERLAY } val gridConfig: GridConfigurationScope.() -> Unit = { repeat(3) { column(GridTrackSize.Fixed(100.dp)) } row(GridTrackSize.Fixed(100.dp)) area(areaId = Areas.BASE, row = 1, column = 1, columnSpan = 2) // Define a named area spanning multiple columns that overlaps other cells area(areaId = Areas.OVERLAY, row = 1, column = 2, columnSpan = 2) }
2. Place base content and declare overlays
Place your base composables in the grid cells using Modifier.gridItem. Grid
follows standard zIndex ordering based on composition order, so items declared
later within the Grid block are drawn on top of earlier items. When placing an
overlay in the same cell or an overlapping area, declare the overlay composable
after the base element, or reference a named area. You can use Modifier.zIndex
if you need to explicitly control or override this default drawing order.
Grid( config = gridConfig, ) { // Base Layer TextCard("BASE", Modifier.gridItem(areaId = Areas.BASE)) // Overlay Layer TextCard( "OVERLAY", Modifier.gridItem(areaId = Areas.OVERLAY), color = Color(0xDD4B608D) ) }
Results
Your UI elements overlay predictably within the 2D grid structure. By relying on standard declaration ordering, you avoid complex nested containers while maintaining clean, responsive layout code.