1---
2title: 'Implementing a checkbox for Expo and React Native apps'
3---
4
5import SnackInline from '~/components/plugins/SnackInline';
6
7One fairly common component that is not offered out of the box by Expo is the mighty checkbox. There are several packages available on npm; however, it is simple enough to implement yourself, and by doing so you have full customization and control over the look and feel of your checkbox.
8
9## Understanding the checkbox
10
11A checkbox is basically a button that exists in one of two states — it is checked or it isn't. This makes it a perfect candidate for the `useState()` hook. Our first iteration will render a button that toggles between checked and unchecked states. When the checkbox is checked, we'll render a checkmark icon in the center of the button.
12
13> You can find more information about using icons in your Expo project in our [Icons guide](/guides/icons/).
14
15<SnackInline>
16
17```jsx
18import React, { useState } from 'react';
19import { Pressable, StyleSheet, Text, View } from 'react-native';
20import Ionicons from '@expo/vector-icons';
21
22function MyCheckbox() {
23  const [checked, onChange] = useState(false);
24
25  function onCheckmarkPress() {
26    onChange(!checked);
27  }
28
29  return (
30    <Pressable
31      style={[styles.checkboxBase, checked && styles.checkboxChecked]}
32      onPress={onCheckmarkPress}>
33      {checked && <Ionicons name="checkmark" size={24} color="white" />}
34    </Pressable>
35  );
36}
37
38export default function App() {
39  return (
40    <View style={styles.appContainer}>
41      <Text style={styles.appTitle}>Checkbox Example</Text>
42
43      <View style={styles.checkboxContainer}>
44        <MyCheckbox />
45        <Text style={styles.checkboxLabel}>{`⬅️ Click!`}</Text>
46      </View>
47    </View>
48  );
49}
50
51const styles = StyleSheet.create({
52  checkboxBase: {
53    width: 24,
54    height: 24,
55    justifyContent: 'center',
56    alignItems: 'center',
57    borderRadius: 4,
58    borderWidth: 2,
59    borderColor: 'coral',
60    backgroundColor: 'transparent',
61  },
62
63  checkboxChecked: {
64    backgroundColor: 'coral',
65  },
66
67  appContainer: {
68    flex: 1,
69    alignItems: 'center',
70  },
71
72  appTitle: {
73    marginVertical: 16,
74    fontWeight: 'bold',
75    fontSize: 24,
76  },
77
78  checkboxContainer: {
79    flexDirection: 'row',
80    alignItems: 'center',
81  },
82
83  checkboxLabel: {
84    marginLeft: 8,
85    fontWeight: 500,
86    fontSize: 18,
87  },
88});
89```
90
91</SnackInline>
92
93> Note: https://icons.expo.fyi is a great resource for finding all of the icons available in the @expo/vector-icons package.
94
95## Controlling the checkbox
96
97This checkbox isn't useful in this state because the `checked` value is accessible only from within the component — more often than not you'll want to control the checkbox from outside. This is achievable by defining `checked` and `onChange` as props that are passed into the checkbox:
98
99<SnackInline>
100
101```jsx
102import React, { useState } from 'react';
103import { Pressable, StyleSheet, Text, View } from 'react-native';
104import Ionicons from '@expo/vector-icons/Ionicons';
105
106function MyCheckbox({
107  /* @info Define checked and onChange as props instead of state */ checked,
108  onChange /* @end */,
109}) {
110  function onCheckmarkPress() {
111    onChange(!checked);
112  }
113
114  return (
115    <Pressable
116      style={[styles.checkboxBase, checked && styles.checkboxChecked]}
117      onPress={onCheckmarkPress}>
118      {checked && <Ionicons name="checkmark" size={24} color="white" />}
119    </Pressable>
120  );
121}
122
123function App() {
124  /* @info Move the checked and onChange values outside of the checkbox component */
125  const [checked, onChange] = useState(false);
126  /* @end */
127
128  return (
129    <View style={styles.appContainer}>
130      <Text style={styles.appTitle}>Checkbox Example</Text>
131
132      <View style={styles.checkboxContainer}>
133        <MyCheckbox
134          /* @info Pass the checked and onChange props to the checkbox */ checked={checked}
135          onChange={onChange} /* @end */
136        />
137        <Text style={styles.checkboxLabel}>{`⬅️ Click!`}</Text>
138      </View>
139    </View>
140  );
141}
142
143export default App;
144
145const styles = StyleSheet.create({
146  checkboxBase: {
147    width: 24,
148    height: 24,
149    justifyContent: 'center',
150    alignItems: 'center',
151    borderRadius: 4,
152    borderWidth: 2,
153    borderColor: 'coral',
154    backgroundColor: 'transparent',
155  },
156
157  checkboxChecked: {
158    backgroundColor: 'coral',
159  },
160
161  appContainer: {
162    flex: 1,
163    alignItems: 'center',
164  },
165
166  appTitle: {
167    marginVertical: 16,
168    fontWeight: 'bold',
169    fontSize: 24,
170  },
171
172  checkboxContainer: {
173    flexDirection: 'row',
174    alignItems: 'center',
175  },
176
177  checkboxLabel: {
178    marginLeft: 8,
179    fontWeight: 500,
180    fontSize: 18,
181  },
182});
183```
184
185</SnackInline>
186
187> Note: This pattern is referred to as a "controlled component" — you can read more about them here: https://reactjs.org/docs/forms.html#controlled-components.
188
189## Extending the interface
190
191It's common enough to need to render different styles when the checkmark is `checked` and when it is not. Let's add this to the checkbox's props and make it more reusable:
192
193<SnackInline>
194
195```jsx
196import React, { useState } from 'react';
197import { Pressable, StyleSheet, Text, View } from 'react-native';
198import Ionicons from '@expo/vector-icons/Ionicons';
199
200function MyCheckbox({
201  checked,
202  onChange,
203  /* @info Add style and icon props to make the checkbox reusable throughout your codebase */ buttonStyle = {},
204  activeButtonStyle = {},
205  inactiveButtonStyle = {},
206  activeIconProps = {},
207  inactiveIconProps = {},
208  /* @end */
209}) {
210  function onCheckmarkPress() {
211    onChange(!checked);
212  }
213
214  /* @info Set icon props based on the checked value */
215  const iconProps = checked ? activeIconProps : inactiveIconProps; /* @end */
216
217  return (
218    <Pressable
219      style={[
220        buttonStyle,
221        /* @info Pass the active / inactive style props to the button based on the current checked value */ checked
222          ? activeButtonStyle
223          : inactiveButtonStyle,
224        /* @end */
225      ]}
226      onPress={onCheckmarkPress}>
227      {checked && (
228        <Ionicons
229          name="checkmark"
230          size={24}
231          color="white"
232          /* @info Pass along any custom icon properties to the Icon component */
233          {...iconProps}
234          /* @end */
235        />
236      )}
237    </Pressable>
238  );
239}
240
241function App() {
242  const [checked, onChange] = useState(false);
243
244  return (
245    <View style={styles.appContainer}>
246      <Text style={styles.appTitle}>Checkbox Example</Text>
247
248      <View style={styles.checkboxContainer}>
249        <MyCheckbox
250          checked={checked}
251          onChange={onChange}
252          /* @info Pass in base and active styles for the checkbox */
253          buttonStyle={styles.checkboxBase}
254          activeButtonStyle={styles.checkboxChecked}
255          /* @end */
256        />
257        <Text style={styles.checkboxLabel}>{`⬅️ Click!`}</Text>
258      </View>
259    </View>
260  );
261}
262
263export default App;
264
265const styles = StyleSheet.create({
266  checkboxBase: {
267    width: 24,
268    height: 24,
269    justifyContent: 'center',
270    alignItems: 'center',
271    borderRadius: 4,
272    borderWidth: 2,
273    borderColor: 'coral',
274    backgroundColor: 'transparent',
275  },
276
277  checkboxChecked: {
278    backgroundColor: 'coral',
279  },
280
281  appContainer: {
282    flex: 1,
283    alignItems: 'center',
284  },
285
286  appTitle: {
287    marginVertical: 16,
288    fontWeight: 'bold',
289    fontSize: 24,
290  },
291
292  checkboxContainer: {
293    flexDirection: 'row',
294    alignItems: 'center',
295  },
296
297  checkboxLabel: {
298    marginLeft: 8,
299    fontWeight: 500,
300    fontSize: 18,
301  },
302});
303```
304
305</SnackInline>
306
307Now this checkbox ticks all of the boxes of what it should be. It toggles between `checked` states, can be controlled, and its styles are fully customizable.
308