1--- 2title: Stacking overlapping views with zIndex in Expo and React Native apps 3sidebar_title: Stacking views with zIndex 4--- 5 6import ImageSpotlight from '~/components/plugins/ImageSpotlight'; 7import SnackEmbed from '~/components/plugins/SnackEmbed'; 8import { Collapsible } from '~/ui/components/Collapsible'; 9 10`zIndex` is the Expo and React Native analog of [CSS's `z-index` property](https://developer.mozilla.org/en-US/docs/Web/CSS/z-index) which lets the developer control the order in which components are displayed over one another. 11 12## Default `zIndex` behavior 13 14Without specifying an explicit `zIndex` or `position`, components that occur later in the tree have a higher z-order. 15 16<SnackEmbed platform="web" preview name="Default layout"> 17 {` 18import React from "react"; 19import { StyleSheet, View } from "react-native";\n 20export default function App() { 21 return ( 22 <View style={styles.root}> 23 <View 24 style={[ 25 styles.container, 26 { 27 backgroundColor: '#e1e4e8', 28 }, 29 ]}> 30 {/* zIndex: 0 */} 31 <View style={[styles.item, { backgroundColor: '#6638f0' }]} /> 32 {/* zIndex: 1 */} 33 <View style={[styles.item, { backgroundColor: '#5cc9f5' }]} /> 34 {/* zIndex: 2 */} 35 <View style={[styles.item, { backgroundColor: '#4af2a1' }]} /> 36 </View> 37 </View> 38 ); 39}\n 40const styles = StyleSheet.create({ 41 root: { 42 flex: 1, 43 backgroundColor: '#fff', 44 alignItems: 'center', 45 justifyContent: 'center', 46 }, 47 container: { 48 height: 200, 49 width: 200, 50 borderRadius: 16, 51 padding: 16, 52 borderWidth: 8, 53 borderColor: 'rgba(0,0,0,0.2)', 54 }, 55 item: { 56 borderWidth: 4, 57 borderColor: 'rgba(0,0,0,0.2)', 58 height: 48, 59 width: 48, 60 borderRadius: 8, 61 }, 62}); 63`} 64</SnackEmbed> 65 66<Collapsible summary="Result"> 67 68<ImageSpotlight 69 style={{ maxWidth: 360 }} 70 alt="Three square components in a square parent container" 71 src="/static/images/z-index/default-layout.png" 72/> 73 74</Collapsible> 75 76This is illustrated more clearly when the components visually intersect each other. 77 78<SnackEmbed platform="web" preview name="Interstecting views"> 79 {` 80import React from "react"; 81import { StyleSheet, View } from "react-native";\n 82export default function App() { 83 return ( 84 <View style={styles.root}> 85 <View 86 style={[ 87 styles.container, 88 { 89 backgroundColor: '#e1e4e8', 90 }, 91 ]}> 92 {/* zIndex: 0 */} 93 <View style={[styles.item, { backgroundColor: '#6638f0' }]} /> 94 {/* zIndex: 1 */} 95 <View style={[styles.item, { backgroundColor: '#5cc9f5', marginTop: -16 }]} /> 96 {/* zIndex: 2 */} 97 <View style={[styles.item, { backgroundColor: '#4af2a1', marginTop: -16 }]} /> 98 </View> 99 </View> 100 ); 101}\n 102const styles = StyleSheet.create({ 103 root: { 104 flex: 1, 105 backgroundColor: '#fff', 106 alignItems: 'center', 107 justifyContent: 'center', 108 }, 109 container: { 110 height: 200, 111 width: 200, 112 borderRadius: 16, 113 padding: 16, 114 borderWidth: 8, 115 borderColor: 'rgba(0,0,0,0.2)', 116 }, 117 item: { 118 borderWidth: 4, 119 borderColor: 'rgba(0,0,0,0.2)', 120 height: 48, 121 width: 48, 122 borderRadius: 8, 123 }, 124}); 125`} 126</SnackEmbed> 127 128<Collapsible summary="Result"> 129 130<ImageSpotlight 131 style={{ maxWidth: 360 }} 132 alt="Three square components intersecting each other" 133 src="/static/images/z-index/default-visually-stacked.png" 134/> 135 136</Collapsible> 137 138## Changing the `zIndex` of an element 139 140If you want to change how a component stacks without changing the order in which it occurs in the component tree, use `zIndex`: 141 142<SnackEmbed platform="web" preview name="zIndex override"> 143 {` 144import React from "react"; 145import { StyleSheet, View } from "react-native";\n 146export default function App() { 147 return ( 148 <View style={styles.root}> 149 <View 150 style={[ 151 styles.container, 152 { 153 backgroundColor: '#e1e4e8', 154 }, 155 ]}> 156 <View style={[styles.item, { backgroundColor: '#6638f0' }]} /> 157 <View style={[styles.item, { backgroundColor: '#5cc9f5', marginTop: -16, zIndex: 1 }]} /> 158 <View style={[styles.item, { backgroundColor: '#4af2a1', marginTop: -16 }]} /> 159 </View> 160 </View> 161 ); 162}\n 163const styles = StyleSheet.create({ 164 root: { 165 flex: 1, 166 backgroundColor: '#fff', 167 alignItems: 'center', 168 justifyContent: 'center', 169 }, 170 container: { 171 height: 200, 172 width: 200, 173 borderRadius: 16, 174 padding: 16, 175 borderWidth: 8, 176 borderColor: 'rgba(0,0,0,0.2)', 177 }, 178 item: { 179 borderWidth: 4, 180 borderColor: 'rgba(0,0,0,0.2)', 181 height: 48, 182 width: 48, 183 borderRadius: 8, 184 }, 185}); 186`} 187</SnackEmbed> 188 189<Collapsible summary="Result"> 190 191<ImageSpotlight 192 style={{ maxWidth: 360 }} 193 alt="Three components where the second is stacked above the first and third" 194 src="/static/images/z-index/relative-z-index.png" 195/> 196 197</Collapsible> 198 199## Manually positioning your component 200 201Along with specifying how the component will stack, you can break out of the default layout set by the component's parent by changing the `position` property on the child component to `'absolute'` and specifying the distance it should be from its parent with the style properties `top`, `right`, `bottom`, and `left`. 202 203<SnackEmbed platform="web" preview name="Position absolute"> 204 {` 205import React from "react"; 206import { StyleSheet, View } from "react-native";\n 207export default function App() { 208 return ( 209 <View style={styles.root}> 210 <View style={[styles.container, { backgroundColor: '#e1e4e8' }]}> 211 <View 212 style={[ 213 styles.item, 214 { backgroundColor: '#6638f0', position: 'absolute', top: 32, left: 32, zIndex: 1 }, 215 ]} 216 /> 217 <View style={[styles.item, { backgroundColor: '#5cc9f5' }]} /> 218 <View style={[styles.item, { backgroundColor: '#4af2a1', marginTop: -16 }]} /> 219 </View> 220 </View> 221 ); 222}\n 223const styles = StyleSheet.create({ 224 root: { 225 flex: 1, 226 backgroundColor: '#fff', 227 alignItems: 'center', 228 justifyContent: 'center', 229 }, 230 container: { 231 height: 200, 232 width: 200, 233 borderRadius: 16, 234 padding: 16, 235 borderWidth: 8, 236 borderColor: 'rgba(0,0,0,0.2)', 237 }, 238 item: { 239 borderWidth: 4, 240 borderColor: 'rgba(0,0,0,0.2)', 241 height: 48, 242 width: 48, 243 borderRadius: 8, 244 }, 245}); 246`} 247</SnackEmbed> 248 249<Collapsible summary="Result"> 250 251<ImageSpotlight 252 style={{ maxWidth: 360 }} 253 alt="Position absolute example" 254 src="/static/images/z-index/absolute-position.png" 255/> 256 257</Collapsible> 258 259You can even make the component extend outside of the parent's visual bounds. 260 261<SnackEmbed platform="web" preview name="Position absolute bounds"> 262 {` 263import React from "react"; 264import { StyleSheet, View } from "react-native";\n 265export default function App() { 266 return ( 267<View style={styles.root}> 268 <View style={[styles.container, { backgroundColor: '#e1e4e8' }]}> 269 <View 270 style={[ 271 styles.item, 272 { backgroundColor: '#6638f0', position: 'absolute', top: -32, left: -32, zIndex: 1 }, 273 ]} 274 /> 275 <View style={[styles.item, { backgroundColor: '#5cc9f5' }]} /> 276 <View style={[styles.item, { backgroundColor: '#4af2a1', marginTop: -16 }]} /> 277 </View> 278 </View> 279 ); 280}\n 281const styles = StyleSheet.create({ 282 root: { 283 flex: 1, 284 backgroundColor: '#fff', 285 alignItems: 'center', 286 justifyContent: 'center', 287 }, 288 container: { 289 height: 200, 290 width: 200, 291 borderRadius: 16, 292 padding: 16, 293 borderWidth: 8, 294 borderColor: 'rgba(0,0,0,0.2)', 295 }, 296 item: { 297 borderWidth: 4, 298 borderColor: 'rgba(0,0,0,0.2)', 299 height: 48, 300 width: 48, 301 borderRadius: 8, 302 }, 303}); 304`} 305</SnackEmbed> 306 307<Collapsible summary="Result"> 308 309<ImageSpotlight 310 style={{ maxWidth: 360 }} 311 alt="Position absolute component out of visual bounds of parent" 312 src="/static/images/z-index/absolute-z-index-bounds.png" 313/> 314 315</Collapsible> 316 317While a `position: 'absolute'` component may seem like it operates independently, it must still respect the `zIndex` of its parent. 318 319<SnackEmbed platform="web" preview name="Parent-child zIndex relationship"> 320 {` 321import React from "react"; 322import { StyleSheet, View } from "react-native";\n 323export default function App() { 324 return ( 325<View style={styles.root}> 326 <View style={[styles.container, { backgroundColor: '#e1e4e8' }]}> 327 <View 328 style={[ 329 styles.item, 330 { backgroundColor: '#6638f0', position: 'absolute', top: -32, left: -32, zIndex: 100 }, 331 ]} 332 /> 333 <View style={[styles.item, { backgroundColor: '#5cc9f5' }]} /> 334 <View style={[styles.item, { backgroundColor: '#4af2a1', marginTop: -16 }]} /> 335 </View> 336 <View style={[styles.container, { backgroundColor: '#dcffe4', marginTop: -188 }]} /> 337 </View> 338 ); 339}\n 340const styles = StyleSheet.create({ 341 root: { 342 flex: 1, 343 backgroundColor: '#fff', 344 alignItems: 'center', 345 justifyContent: 'center', 346 }, 347 container: { 348 height: 200, 349 width: 200, 350 borderRadius: 16, 351 padding: 16, 352 borderWidth: 8, 353 borderColor: 'rgba(0,0,0,0.2)', 354 }, 355 item: { 356 borderWidth: 4, 357 borderColor: 'rgba(0,0,0,0.2)', 358 height: 48, 359 width: 48, 360 borderRadius: 8, 361 }, 362}); 363`} 364</SnackEmbed> 365 366<Collapsible summary="Result"> 367 368<ImageSpotlight 369 style={{ maxWidth: 360 }} 370 alt="Position absolute child must respect parent's zIndex" 371 src="/static/images/z-index/z-index-parent.png" 372/> 373 374</Collapsible> 375