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
| Prop | Type | Default | Required | Description |
|---|---|---|---|---|
inverted | boolean | | No | Reverses the layout direction (`column-reverse`). |
p | SpacingValue | - | No | Padding on all sides |
px | SpacingValue | - | No | Horizontal padding (paddingStart + paddingEnd) |
py | SpacingValue | - | No | Vertical padding (paddingTop + paddingBottom) |
pt | SpacingValue | - | No | Padding top |
pb | SpacingValue | - | No | Padding bottom |
ps | SpacingValue | - | No | Padding start (left in LTR, right in RTL) |
pe | SpacingValue | - | No | Padding end (right in LTR, left in RTL) |
m | SpacingValue | - | No | Margin on all sides |
mx | SpacingValue | - | No | Horizontal margin (marginStart + marginEnd) |
my | SpacingValue | - | No | Vertical margin (marginTop + marginBottom) |
mt | SpacingValue | - | No | Margin top |
mb | SpacingValue | - | No | Margin bottom |
ms | SpacingValue | - | No | Margin start |
me | SpacingValue | - | No | Margin end |
gap | SpacingValue | - | No | Gap between children |
rowGap | SpacingValue | - | No | Row gap between children |
columnGap | SpacingValue | - | No | Column gap between children |
flex | number | - | No | Flex value |
align | enum | - | No | Align items along the cross axis |
justify | enum | - | No | Justify content along the main axis |
bg | string | - | No | Background color |
hrefAttrs | { target?: string; rel?: string; download?: string | boolean; } | - | No | Web 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 | - |