Arrangement and Alignment in Jetpack Compose

Quick answer:
Arrangementdistributes children on a layout’s main axis.Alignmentpositions children on its cross axis. In aRow, that means horizontal arrangement and vertical alignment; in aColumn, vertical arrangement and horizontal alignment. ABoxuses two-dimensional alignment.
When a Compose layout looks almost right but its content is stuck in the wrong place, first ask two questions: what is this container’s main axis, and does it have any extra space to distribute? Those answers make Arrangement and Alignment predictable.
This guide builds on Row vs Column vs Box in Jetpack Compose, which explains which container to choose. Here, the focus is on placing the children inside it.
Main axis versus cross axis
Every Row and Column has two directions:
| Container | Main axis | Cross axis | Arrangement controls | Alignment controls | | --- | --- | --- | --- | | Row | Horizontal | Vertical | horizontalArrangement | verticalAlignment | | Column | Vertical | Horizontal | verticalArrangement | horizontalAlignment | | Box | Both dimensions | Both dimensions | Not applicable | contentAlignment or child align() |
The Compose layout basics documentation uses this same division: Row configures its children with a horizontal arrangement and vertical alignment, while Column does the reverse.
Arrange children along the main axis
Arrangement answers: how should this container distribute children in its primary direction?
For a Row, common choices include:
Arrangement.Start— pack children at the start of the horizontal direction.Arrangement.End— pack children at the end.Arrangement.Center— group children in the middle.Arrangement.SpaceBetween— put the first and last child at the edges and distribute only the remaining space between items.Arrangement.SpaceAround— give edge items half the gap that exists between neighboring items.Arrangement.SpaceEvenly— give the same gap before, between, and after children.Arrangement.spacedBy(12.dp)— put a fixed gap between adjacent children.
Column offers the same general distribution choices on the vertical axis, including Arrangement.Top and Arrangement.Bottom. The Arrangement API reference defines the exact edge behavior for each option.
Use spacedBy() for deliberate component gaps
Fixed gaps are usually clearer than relying on whatever free space happens to remain in the parent.
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
fun ProfileActions(
onFollow: () -> Unit,
onMessage: () -> Unit,
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(12.dp),
) {
Button(onClick = onFollow) {
Text("Follow")
}
Button(onClick = onMessage) {
Text("Message")
}
}
}Use the parent modifier for outside space and spacedBy() for the gap between siblings. In other words, Modifier.padding(horizontal = 16.dp) creates a boundary around the Row; Arrangement.spacedBy(12.dp) creates a repeated relationship between its children.
Use space-distribution arrangements only when space can exist
SpaceBetween, SpaceAround, and SpaceEvenly need unused main-axis space to distribute. A Row that is only as wide as its children has no extra width; a Column that wraps its contents has no extra height.
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@Composable
fun BottomActionScreen(onContinue: () -> Unit) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.SpaceBetween,
) {
Text("Choose how you want to continue")
Button(onClick = onContinue) {
Text("Continue")
}
}
}fillMaxSize() gives the Column the available screen space, so SpaceBetween can put the title at the top and the button at the bottom. Without available height, the two items simply remain together.
Align children across the cross axis
Alignment answers: where should children sit in the direction perpendicular to the main axis?
For a profile row, centering an avatar against a two-line text block is a common need:
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.background
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
@Composable
fun ProfileSummary() {
Row(
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Box(
modifier = Modifier
.size(48.dp)
.background(Color.LightGray, CircleShape),
)
Column {
Text("Ada Lovelace")
Text(
text = "Online now",
modifier = Modifier.padding(top = 2.dp),
)
}
}
}verticalAlignment = Alignment.CenterVertically applies one rule to every direct child of the Row. For Column, use horizontalAlignment = Alignment.CenterHorizontally, Alignment.Start, or Alignment.End instead.
Choose Start and End for app UI that should respect layout direction. They adapt to right-to-left locales, unlike physical Left and Right assumptions.
Use Box for overlays and two-dimensional placement
A Box stacks its children. Set contentAlignment when most children share a position, then override an individual direct child with the scoped Modifier.align().
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
@Composable
fun AvatarWithStatus() {
Box(
modifier = Modifier.size(96.dp),
contentAlignment = Alignment.Center,
) {
AvatarPlaceholder()
Box(
modifier = Modifier
.align(Alignment.BottomEnd)
.size(20.dp)
.background(Color(0xFF2E7D32), CircleShape),
)
}
}
@Composable
private fun AvatarPlaceholder() {
Box(
modifier = Modifier
.size(72.dp)
.background(Color.LightGray, CircleShape),
)
}align() is a scoped modifier: it has meaning only for a direct child of a parent that provides the matching scope, such as BoxScope. This is one reason Compose modifiers are safer than a long collection of XML layout attributes. See Jetpack Compose Modifier: A Practical Guide for more on scoped modifiers and chain order.
Choose the right tool for common layout goals
| Goal | Prefer | Why |
|---|---|---|
| Even fixed gap between sibling items | Arrangement.spacedBy() | The gap is explicit and does not depend on screen size. |
| Toolbar action at the opposite edge | Arrangement.SpaceBetween in a full-width Row | Uses available horizontal space between edge children. |
| Content and primary action at opposite screen edges | Arrangement.SpaceBetween in a full-height Column | Uses available vertical space. |
Center a whole group in a Row or Column | Arrangement.Center | Positions the group on the main axis. |
| Center every row child vertically | verticalAlignment = Alignment.CenterVertically | Applies to the Row’s cross axis. |
Move just one direct child within a Column or Box | Scoped Modifier.align() | Overrides the parent’s default for that child. |
| Put a badge over an image | Box plus Modifier.align(Alignment.TopEnd) | An overlay is a two-dimensional placement problem. |
Arrangement in lazy lists
Lazy layouts use the same idea. LazyColumn accepts verticalArrangement; LazyRow accepts horizontalArrangement. Use spacedBy() for a stable item gap, then use contentPadding for the inset around the list’s whole content.
LazyColumn(
contentPadding = PaddingValues(horizontal = 16.dp, vertical = 12.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
// items(...)
}This avoids a common mistake: adding padding to every item to simulate list spacing, which can create unwanted edge gaps or make click targets harder to reason about. The official lazy layout guide documents Arrangement.spacedBy() for lazy lists; for a full list introduction, read Jetpack Compose LazyColumn.
Common mistakes
Expecting Center to move content in a wrapping parent
Arrangement.Center or Alignment.CenterHorizontally can only position content inside the bounds the parent has. Give the parent meaningful available width or height when you expect visible movement.
Using child padding for every sibling gap
Padding belongs to an individual child; arrangement belongs to the relationship among siblings. Prefer spacedBy() when the gap should be uniform across the container.
Using SpaceBetween as a replacement for a fixed design gap
SpaceBetween grows and shrinks with available space. Use it for edge-to-edge distribution, not for a card layout that needs a consistent 12dp gap.
Forgetting that align() is scoped
Modifier.align() must be used by the correct direct child. If it appears not to work, check whether another wrapper sits between that child and the Row, Column, or Box that provides the scope.
FAQ
What is the difference between Arrangement and Alignment?
Arrangement distributes children on the main axis: horizontal in a Row, vertical in a Column. Alignment positions children on the cross axis: vertical in a Row, horizontal in a Column.
When should I use spacedBy() instead of SpaceBetween?
Use spacedBy() when the gap between neighbors must remain a fixed value. Use SpaceBetween when the first and last children should sit at the container edges and consume the remaining space between them.
Why does my Column ignore SpaceBetween?
It usually has no extra height. Give it available height through its parent constraints or an appropriate size modifier such as fillMaxSize() before expecting a space-distribution arrangement to be visible.