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