xref: /expo/docs/pages/ui-programming/z-index.mdx (revision dfd15ebd)
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