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