Skip to main content

Column

Lays children out vertically. Extends Box with all spacing, alignment, and gap props.

Usage​

import { Button, Column, Typography } from '@rootnative/components'
import { mdiResolver } from '@rootnative/components/mdi'
import { ThemeProvider } from '@rootnative/core'
import { SafeAreaProvider } from 'react-native-safe-area-context'
import { View } from 'react-native'

export default function App() {
return (
<SafeAreaProvider>
<ThemeProvider iconResolver={mdiResolver}>
<View style={{ flex: 1, justifyContent: 'center' }}>
<Column gap="md" p="lg">
<Typography variant="headlineSmall">Title</Typography>
<Typography variant="bodyMedium">Description text</Typography>
<Button variant="filled" onPress={() => {}}>Action</Button>
</Column>
</View>
</ThemeProvider>
</SafeAreaProvider>
)
}

Inverted​

Reverse the layout direction with inverted:

import { Column, Typography } from '@rootnative/components'
import { mdiResolver } from '@rootnative/components/mdi'
import { ThemeProvider } from '@rootnative/core'
import { SafeAreaProvider } from 'react-native-safe-area-context'
import { View } from 'react-native'

export default function App() {
return (
<SafeAreaProvider>
<ThemeProvider iconResolver={mdiResolver}>
<View style={{ flex: 1, justifyContent: 'center', padding: 16 }}>
<Column inverted gap="sm">
<Typography>First</Typography>
<Typography>Second</Typography>
</Column>
</View>
</ThemeProvider>
</SafeAreaProvider>
)
}

Wrapping a child in your own layer​

align maps straight to flexbox alignItems, so align="center" sizes every child to its own content on the cross axis rather than stretching it to the column's width. That is the correct behaviour, and it is invisible until you put a wrapper between the Column and its child — most often an animation layer:

// The Typography centres its own text, but the wrapper has shrunk to fit that
// text, so there is nothing left to centre it against.
<Column align="center">
<Motion.View initial={{ opacity: 0 }} animate={{ opacity: 1 }}>
<Typography variant="displayMedium">Centred heading</Typography>
</Motion.View>
</Column>

The wrapper is now a child of the column, so it — not the text — is what align="center" sizes. Give the wrapper alignSelf: 'stretch' and the inner block gets the full width back:

<Column align="center">
<Motion.View
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
style={styles.stretch}
>
<Typography variant="displayMedium">Centred heading</Typography>
</Motion.View>
</Column>

// const styles = StyleSheet.create({ stretch: { alignSelf: 'stretch' } })

This is ordinary flexbox rather than a quirk of Column, but it is the predictable cost of inserting a layer of your own: any wrapper you add becomes the aligned child, and anything the original child did with its own width now measures against the wrapper instead. It applies to a child that centres its own text, one that lays out its own row, and any block sized as a share of the available width.

Row behaves the same way on its cross axis, where align controls height instead — less often noticed, because a wrapper shrinking to its content height usually looks correct.

Props​

PropTypeDefaultRequiredDescription
invertedbooleanNoReverses the layout direction (`column-reverse`).
pSpacingValue-NoPadding on all sides
pxSpacingValue-NoHorizontal padding (paddingStart + paddingEnd)
pySpacingValue-NoVertical padding (paddingTop + paddingBottom)
ptSpacingValue-NoPadding top
pbSpacingValue-NoPadding bottom
psSpacingValue-NoPadding start (left in LTR, right in RTL)
peSpacingValue-NoPadding end (right in LTR, left in RTL)
mSpacingValue-NoMargin on all sides
mxSpacingValue-NoHorizontal margin (marginStart + marginEnd)
mySpacingValue-NoVertical margin (marginTop + marginBottom)
mtSpacingValue-NoMargin top
mbSpacingValue-NoMargin bottom
msSpacingValue-NoMargin start
meSpacingValue-NoMargin end
gapSpacingValue-NoGap between children
rowGapSpacingValue-NoRow gap between children
columnGapSpacingValue-NoColumn gap between children
flexnumber-NoFlex value
alignenum-NoAlign items along the cross axis
justifyenum-NoJustify content along the main axis
bgstring-NoBackground color
hrefAttrs{ target?: string; rel?: string; download?: string | boolean; }-NoWeb only. The `target`, `rel` and `download` of the `<a>` that a view with an `href` renders, for example under Expo Router's `<Link asChild>`. `target: '_blank'` opens the link in a new tab. Native ignores it.
onKeyDown(event: { nativeEvent: { key?: string; }; }) => void-No-