--- title: Implement a checkbox description: Learn how to implement a fully customizable checkbox in your Expo project. --- import { SnackInline } from '~/ui/components/Snippet'; The [`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. ## Understanding the checkbox A 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. > You can find more information about using icons in your Expo project in our [Icons guide](/guides/icons). ```jsx import { useState } from 'react'; import { Pressable, StyleSheet, Text, View } from 'react-native'; import { Ionicons } from '@expo/vector-icons'; function MyCheckbox() { const [checked, setChecked] = useState(false); return ( setChecked(!checked)}> {checked && } ); } export default function App() { return ( Checkbox Example {`⬅️ Click!`} ); } const styles = StyleSheet.create({ checkboxBase: { width: 24, height: 24, justifyContent: 'center', alignItems: 'center', borderRadius: 4, borderWidth: 2, borderColor: 'coral', backgroundColor: 'transparent', }, checkboxChecked: { backgroundColor: 'coral', }, appContainer: { flex: 1, alignItems: 'center', justifyContent: 'center', }, appTitle: { marginVertical: 16, fontWeight: 'bold', fontSize: 24, }, checkboxContainer: { flexDirection: 'row', alignItems: 'center', }, checkboxLabel: { marginLeft: 8, fontWeight: 500, fontSize: 18, }, }); ``` > [icons.expo.fyi](https://icons.expo.fyi) is a great resource for finding all of the icons available in the `@expo/vector-icons` package. ## Controlling the checkbox This 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: {/* prettier-ignore */} ```jsx import { useState } from 'react'; import { Pressable, StyleSheet, Text, View } from 'react-native'; import { Ionicons } from '@expo/vector-icons'; function MyCheckbox({ /* @info Define checked and onChange as props instead of state */onChange, checked/* @end */ }) { return ( {checked && } ); } export default function App() { /* @info Move the checked and setChecked values outside of the checkbox component */ const [checked, setChecked] = useState(false); /* @end */ return ( Checkbox Example /* @info Pass the checked and onChange props to the checkbox */ setChecked(!checked)} checked={checked} /> /* @end */ {`⬅️ Click!`} ); } const styles = StyleSheet.create({ checkboxBase: { width: 24, height: 24, justifyContent: 'center', alignItems: 'center', borderRadius: 4, borderWidth: 2, borderColor: 'coral', backgroundColor: 'transparent', }, checkboxChecked: { backgroundColor: 'coral', }, appContainer: { flex: 1, alignItems: 'center', justifyContent: 'center', }, appTitle: { marginVertical: 16, fontWeight: 'bold', fontSize: 24, }, checkboxContainer: { flexDirection: 'row', alignItems: 'center', }, checkboxLabel: { marginLeft: 8, fontWeight: 500, fontSize: 18, }, }); ``` > This pattern is referred to as a [controlled component](https://react.dev/learn/sharing-state-between-components#controlled-and-uncontrolled-components). ## Extending the interface It'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: ```jsx import { useState } from 'react'; import { Pressable, StyleSheet, Text, View } from 'react-native'; import { Ionicons } from '@expo/vector-icons'; function MyCheckbox({ checked, onChange, /* @info Add style and icon props to make the checkbox reusable throughout your codebase */ buttonStyle = {}, activeButtonStyle = {}, inactiveButtonStyle = {}, activeIconProps = {}, inactiveIconProps = {}, /* @end */ }) { /* @info Set icon props based on the checked value */ const iconProps = checked ? activeIconProps : inactiveIconProps; /* @end */ return ( onChange(!checked)}> {checked && ( )} ); } export default function App() { const [checked, setChecked] = useState(false); return ( Checkbox Example {`⬅️ Click!`} ); } const styles = StyleSheet.create({ checkboxBase: { width: 24, height: 24, justifyContent: 'center', alignItems: 'center', borderRadius: 4, borderWidth: 2, borderColor: 'coral', backgroundColor: 'transparent', }, checkboxChecked: { backgroundColor: 'coral', }, appContainer: { flex: 1, alignItems: 'center', justifyContent: 'center', }, appTitle: { marginVertical: 16, fontWeight: 'bold', fontSize: 24, }, checkboxContainer: { flexDirection: 'row', alignItems: 'center', }, checkboxLabel: { marginLeft: 8, fontWeight: 500, fontSize: 18, }, }); ``` This 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.