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