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
8The [`expo-checkbox`](/versions/latest/sdk/checkbox/) package provides a quick implementation of a checkbox that you can directly use in your project. However, to have full customization, and control over the look and feel of the checkbox, this page goes in-depth on how to implement the component from scratch.
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 dependencies={['@expo/vector-icons']}>
17
18```jsx
19import { useState } from 'react';
20import { Pressable, StyleSheet, Text, View } from 'react-native';
21import { Ionicons } from '@expo/vector-icons';
22
23function MyCheckbox() {
24  const [checked, setChecked] = useState(false);
25  return (
26    <Pressable
27      style={[styles.checkboxBase, checked && styles.checkboxChecked]}
28      onPress={() => setChecked(!checked)}>
29      {checked && <Ionicons name="checkmark" size={24} color="white" />}
30    </Pressable>
31  );
32}
33
34export default function App() {
35  return (
36    <View style={styles.appContainer}>
37      <Text style={styles.appTitle}>Checkbox Example</Text>
38      <View style={styles.checkboxContainer}>
39        <MyCheckbox />
40        <Text style={styles.checkboxLabel}>{`⬅️ Click!`}</Text>
41      </View>
42    </View>
43  );
44}
45
46const styles = StyleSheet.create({
47  checkboxBase: {
48    width: 24,
49    height: 24,
50    justifyContent: 'center',
51    alignItems: 'center',
52    borderRadius: 4,
53    borderWidth: 2,
54    borderColor: 'coral',
55    backgroundColor: 'transparent',
56  },
57  checkboxChecked: {
58    backgroundColor: 'coral',
59  },
60  appContainer: {
61    flex: 1,
62    alignItems: 'center',
63    justifyContent: 'center',
64  },
65  appTitle: {
66    marginVertical: 16,
67    fontWeight: 'bold',
68    fontSize: 24,
69  },
70  checkboxContainer: {
71    flexDirection: 'row',
72    alignItems: 'center',
73  },
74  checkboxLabel: {
75    marginLeft: 8,
76    fontWeight: 500,
77    fontSize: 18,
78  },
79});
80```
81
82</SnackInline>
83
84> [icons.expo.fyi](https://icons.expo.fyi) is a great resource for finding all of the icons available in the `@expo/vector-icons` package.
85
86## Controlling the checkbox
87
88This 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:
89
90<SnackInline dependencies={['@expo/vector-icons']}>
91
92{/* prettier-ignore */}
93```jsx
94import { useState } from 'react';
95import { Pressable, StyleSheet, Text, View } from 'react-native';
96import { Ionicons } from '@expo/vector-icons';
97
98function MyCheckbox({ /* @info Define checked and onChange as props instead of state */onChange, checked/* @end */ }) {
99  return (
100    <Pressable
101      style={[styles.checkboxBase, checked && styles.checkboxChecked]}
102      onPress={onChange}>
103      {checked && <Ionicons name="checkmark" size={24} color="white" />}
104    </Pressable>
105  );
106}
107
108export default function App() {
109  /* @info Move the checked and setChecked values outside of the checkbox component */
110  const [checked, setChecked] = useState(false);
111  /* @end */
112  return (
113    <View style={styles.appContainer}>
114      <Text style={styles.appTitle}>Checkbox Example</Text>
115      <View style={styles.checkboxContainer}>
116        /* @info Pass the checked and onChange props to the checkbox */
117        <MyCheckbox onChange={() => setChecked(!checked)} checked={checked} />
118        /* @end */
119        <Text style={styles.checkboxLabel}>{`⬅️ Click!`}</Text>
120      </View>
121    </View>
122  );
123}
124
125const styles = StyleSheet.create({
126  checkboxBase: {
127    width: 24,
128    height: 24,
129    justifyContent: 'center',
130    alignItems: 'center',
131    borderRadius: 4,
132    borderWidth: 2,
133    borderColor: 'coral',
134    backgroundColor: 'transparent',
135  },
136  checkboxChecked: {
137    backgroundColor: 'coral',
138  },
139  appContainer: {
140    flex: 1,
141    alignItems: 'center',
142    justifyContent: 'center',
143  },
144  appTitle: {
145    marginVertical: 16,
146    fontWeight: 'bold',
147    fontSize: 24,
148  },
149  checkboxContainer: {
150    flexDirection: 'row',
151    alignItems: 'center',
152  },
153  checkboxLabel: {
154    marginLeft: 8,
155    fontWeight: 500,
156    fontSize: 18,
157  },
158});
159```
160
161</SnackInline>
162
163> This pattern is referred to as a [controlled component](https://react.dev/learn/sharing-state-between-components#controlled-and-uncontrolled-components).
164
165## Extending the interface
166
167It'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:
168
169<SnackInline dependencies={['@expo/vector-icons']}>
170
171```jsx
172import { useState } from 'react';
173import { Pressable, StyleSheet, Text, View } from 'react-native';
174import { Ionicons } from '@expo/vector-icons';
175
176function MyCheckbox({
177  checked,
178  onChange,
179  /* @info Add style and icon props to make the checkbox reusable throughout your codebase */
180  buttonStyle = {},
181  activeButtonStyle = {},
182  inactiveButtonStyle = {},
183  activeIconProps = {},
184  inactiveIconProps = {},
185  /* @end */
186}) {
187  /* @info Set icon props based on the checked value */
188  const iconProps = checked ? activeIconProps : inactiveIconProps;
189  /* @end */
190  return (
191    <Pressable
192      style={[
193        buttonStyle,
194        /* @info Pass the active / inactive style props to the button based on the current checked value */ checked
195          ? activeButtonStyle
196          : inactiveButtonStyle,
197        /* @end */
198      ]}
199      onPress={() => onChange(!checked)}>
200      {checked && (
201        <Ionicons
202          name="checkmark"
203          size={24}
204          color="white"
205          /* @info Pass along any custom icon properties to the Icon component */
206          {...iconProps}
207          /* @end */
208        />
209      )}
210    </Pressable>
211  );
212}
213
214export default function App() {
215  const [checked, setChecked] = useState(false);
216  return (
217    <View style={styles.appContainer}>
218      <Text style={styles.appTitle}>Checkbox Example</Text>
219      <View style={styles.checkboxContainer}>
220        <MyCheckbox
221          checked={checked}
222          onChange={setChecked}
223          /* @info Pass in base and active styles for the checkbox */
224          buttonStyle={styles.checkboxBase}
225          activeButtonStyle={styles.checkboxChecked}
226          /* @end */
227        />
228        <Text style={styles.checkboxLabel}>{`⬅️ Click!`}</Text>
229      </View>
230    </View>
231  );
232}
233
234const styles = StyleSheet.create({
235  checkboxBase: {
236    width: 24,
237    height: 24,
238    justifyContent: 'center',
239    alignItems: 'center',
240    borderRadius: 4,
241    borderWidth: 2,
242    borderColor: 'coral',
243    backgroundColor: 'transparent',
244  },
245  checkboxChecked: {
246    backgroundColor: 'coral',
247  },
248  appContainer: {
249    flex: 1,
250    alignItems: 'center',
251    justifyContent: 'center',
252  },
253  appTitle: {
254    marginVertical: 16,
255    fontWeight: 'bold',
256    fontSize: 24,
257  },
258  checkboxContainer: {
259    flexDirection: 'row',
260    alignItems: 'center',
261  },
262  checkboxLabel: {
263    marginLeft: 8,
264    fontWeight: 500,
265    fontSize: 18,
266  },
267});
268```
269
270</SnackInline>
271
272This checkbox ticks all of the boxes of what it should be. It toggles between `checked` states, can be controlled, and the styles are fully customizable.
273