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