Expokit
AboutDocsComponentsChartsBlocksPricing
Sign inGet started
All components

Inputs

CalendarNewDate PickerNewFloating InputOTP InputPassword InputSearch BarSelectSliderStepperTextareaTime PickerNew

Selection

CheckboxChip GroupRadio GroupRatingProSegmented ControlSwitch

Buttons

ButtonFloating Action ButtonProMorphing Button GroupProQR Code ButtonProSlide to ConfirmProStateful Button

Feedback

AlertProgressSkeletonSpinnerToast

Overlays

Action SheetNewBottom SheetDialogDrawerNew

Display

AccordionAvatarBadgeCardCarouselProGrain GradientNewList ItemNewSwipeable RowProTimelineNew

Navigation

Bottom NavNewProgress StepsNewTabs

Motion

Number TickerProOrb RevealProText Animation
Checkbox
Sponsor ExpoKit

Free stays free. Tip once — keep shipping.

Support
About
Docs
IntroductionWhat ExpoKit is and how it worksInstallationAdd ExpoKit to an Expo appThemingColours, radius and dark modeCLIinit, add, login and updateAI assistants (MCP)Cursor, Claude, Antigravity and moreRun on your phoneTry components in Expo GoChangelogWhat shipped recently
npx @expokit/cli@latest initQuick start
Components
Liquid Glass8 iOS 26 glass pieces3DNew14 tilt, flip and stack
Inputs 11
  • CalendarNew
  • Date PickerNew
  • Floating Input
  • OTP Input
  • Password Input
  • +6 more
Selection 6
  • Checkbox
  • Chip Group
  • Radio Group
  • RatingPro
  • Segmented Control
  • +1 more
Buttons 6
  • Button
  • Floating Action ButtonPro
  • Morphing Button GroupPro
  • QR Code ButtonPro
  • Slide to ConfirmPro
  • +1 more
Feedback 5
  • Alert
  • Progress
  • Skeleton
  • Spinner
  • Toast
Overlays 4
  • Action SheetNew
  • Bottom Sheet
  • Dialog
  • DrawerNew
Display 9
  • Accordion
  • Avatar
  • Badge
  • Card
  • CarouselPro
  • +4 more
Navigation 3
  • Bottom NavNew
  • Progress StepsNew
  • Tabs
Motion 3
  • Number TickerPro
  • Orb RevealPro
  • Text Animation
68 components · 329 live variantsAll components
Charts

Cartesian

  • Area Chart
  • Bar Chart
  • Line Chart
  • Combo Chart

Circular

  • Pie & Donut
  • Radial Chart
  • Radar Chart
  • Polar AreaPro

3D & Pixel

  • Isometric BarsPro
  • Pixel BarsPro

Specialty

  • TreemapPro
  • WaterfallPro
  • BulletPro
  • TimelinePro
  • Gauge
  • Candlestick
  • Heatmap
  • Funnel

Classic

  • Sparkline
  • Progress Rings
  • MultiLine (classic)
  • StackedBar (classic)
  • Ranking Bars (classic)

Guides

  • Usage & config
  • Patterns & effects
  • Show / hide anything
  • Chart theming
  • Interactions
23 charts, built on the open-source expo-charts engineAll charts
Blocks

Screens

ChatProAn AI assistant chat screenCheckoutProA premium checkoutDashboardProA finance / analytics home screenDonut ShopProA dessert shop screen where the products rollInboxProA messaging inboxOnboardingProA minimal swipeable welcome flowProfileProAccount profile for any appSearch ResultsProA clean, monochrome search screenSettingsProAn iOS-style grouped settings screenSign InProOne configurable sign-in flow modelled on top mobile apps

App templates

  • AI assistant appPro
  • Business banking appPro
  • Food ordering appPro
10 complete screens — drop one in, wire your dataSee them on devices All templates All blocks
Pricing
DocsComponentsSelectionCheckbox

Checkbox

Open in Expo Go

Scan with Expo Go

Checkbox with all its variants, native in Expo Go. No account needed.

Needs Expo Go for SDK 58 — update from the App Store or Play Store.

No Expo Go? Open in your browser

A checkbox whose tick draws itself in as the box squishes and a ring bursts out.

<Checkbox />4 variantsiOS · Android · Expo Go
9:41

Default

Checkbox

Installation#

One command copies the source into your app — you own the code and can change anything.

Usage#

Colours, radius and light / dark come from useKit(). Wrap your app in <KitProvider accent="#6366f1" radius={14}> to re-skin every component at once.

Props#

PropTypeDefault
checked / onChange

Controlled state (or use defaultChecked).

boolean—
defaultChecked

Initial state when uncontrolled.

booleanfalse
label

Text next to the box.

string—
description

Secondary line.

string—
strike

Strike the label through when checked.

booleanfalse
shape

Rounded square (corner = size / 3.4) or full circle.

"square" | "circle""square"
size

Box width and height in points; the tick is 80% of it.

number24
accent

Checked colour.

stringtheme accent
disabled

Dims to 50% and blocks presses.

booleanfalse
style

Style for the pressable row holding the box and label.

StyleProp<ViewStyle>—

Behaviour & accessibility#

Touch
The whole row toggles. The box squishes on press-in and bounces back on release.
Motion
The fill and tick spring in together, and a ring bursts outward each time it becomes checked.
Screen readers
A checkbox with checked and disabled state.

Source#

PreviousTime PickerNextChip Group

Did you like the content?

Applies to every preview on the site.

Accent
Radius
Font
9:41

Default

Checkbox

Manual installation
  1. Install dependencies

    terminal
    npx expo install react-native-svg
  2. Copy the files

    Create each file at the same path and paste its code. @/ points at your src/ folder (the default in new Expo apps). Skip any file you already have, like lib/kit.tsx.

    • src/components/ui/checkbox.tsx152 lines
    • src/lib/kit.tsx223 lines

    Want to read it first? Every file is in Source below.

  3. Use it

    App.tsx
    import { Checkbox } from "@/components/ui/checkbox";

    See Usage for a full example.

Example.tsx
import { Checkbox } from '@/components/ui/checkbox'; <Checkbox  label="Email me about new releases"  description="About once a month, never spam."  defaultChecked/>

Every variant above has a complete, runnable example in its Code tab.

src/components/ui/checkbox.tsx
import React, { useEffect, useRef, useState } from 'react';import { Animated, Pressable, StyleSheet, Text, View, type StyleProp, type ViewStyle } from 'react-native';import Svg, { Path } from 'react-native-svg'; import { alpha, useKit } from '@/lib/kit'; const AnimatedPath = Animated.createAnimatedComponent(Path);const CHECK = 'M5 12.5l4.5 4.5L19 7.5';const CHECK_LEN = 22; export interface CheckboxProps {  checked?: boolean;  defaultChecked?: boolean;  onChange?: (checked: boolean) => void;  label?: string;  description?: string;  /** Strike through the label when checked (to-do list style). */  strike?: boolean;  size?: number;  accent?: string;  disabled?: boolean;  /** 'square' (default) or 'circle'. */  shape?: 'square' | 'circle';  style?: StyleProp<ViewStyle>;} /** Checkbox whose tick draws itself in, with a squishy press and a burst ring. */export function Checkbox({  checked,  defaultChecked = false,  onChange,  label,  description,  strike,  size = 24,  accent,  disabled,  shape = 'square',  style,}: CheckboxProps) {  const { colors, radius, font } = useKit();  const ff = font.family ? { fontFamily: font.family } : null;  const tint = accent ?? colors.accent;  const corner = shape === 'circle' ? size / 2 : Math.min(size / 2, radius.sm + 1);  const [inner, setInner] = useState(defaultChecked);  const on = checked ?? inner;   const progress = useRef(new Animated.Value(on ? 1 : 0)).current; // fill + tick (JS: drives svg + colour)  const press = useRef(new Animated.Value(1)).current;  const burst = useRef(new Animated.Value(0)).current;   useEffect(() => {    Animated.spring(progress, { toValue: on ? 1 : 0, useNativeDriver: false, speed: 14, bounciness: 0 }).start();    if (on) {      burst.setValue(0);      Animated.timing(burst, { toValue: 1, duration: 450, useNativeDriver: true }).start();    }  }, [on, progress, burst]);   const toggle = () => {    if (checked === undefined) setInner(!on);    onChange?.(!on);  };   const border = colors.input;  const fg = colors.foreground;  const muted = colors.mutedForeground;   return (    <Pressable      accessibilityRole="checkbox"      accessibilityState={{ checked: on, disabled }}      disabled={disabled}      onPress={toggle}      onPressIn={() => Animated.spring(press, { toValue: 0.82, useNativeDriver: true, speed: 50 }).start()}      onPressOut={() => Animated.spring(press, { toValue: 1, useNativeDriver: true, speed: 20, bounciness: 0 }).start()}      style={[styles.row, { opacity: disabled ? 0.5 : 1 }, style]}    >      <Animated.View style={{ transform: [{ scale: press }] }}>        <Animated.View          pointerEvents="none"          style={[            styles.burst,            {              width: size,              height: size,              borderRadius: corner,              borderColor: tint,              opacity: burst.interpolate({ inputRange: [0, 0.2, 1], outputRange: [0, 0.6, 0] }),              transform: [{ scale: burst.interpolate({ inputRange: [0, 1], outputRange: [1, 1.9] }) }],            },          ]}        />        <Animated.View          style={[            styles.box,            {              width: size,              height: size,              borderRadius: corner,              borderColor: progress.interpolate({ inputRange: [0, 1], outputRange: [border, tint] }),              backgroundColor: progress.interpolate({ inputRange: [0, 1], outputRange: [alpha(tint, 0), tint] }),            },          ]}        >          <Svg width={size * 0.8} height={size * 0.8} viewBox="0 0 24 24">            <AnimatedPath              d={CHECK}              stroke="#fff"              strokeWidth={3}              strokeLinecap="round"              strokeLinejoin="round"              fill="none"              strokeDasharray={CHECK_LEN}              strokeDashoffset={progress.interpolate({ inputRange: [0, 1], outputRange: [CHECK_LEN, 0], extrapolate: 'clamp' }) as unknown as number}            />          </Svg>        </Animated.View>      </Animated.View>      {label ? (        <View style={styles.text}>          <View style={styles.labelBox}>            <Animated.Text style={[styles.label, ff, { color: progress.interpolate({ inputRange: [0, 1], outputRange: [fg, strike ? muted : fg] }) }]}>              {label}            </Animated.Text>            {strike ? (              <Animated.View                style={[                  styles.strike,                  { backgroundColor: muted, width: progress.interpolate({ inputRange: [0, 1], outputRange: ['0%', '100%'], extrapolate: 'clamp' }) },                ]}              />            ) : null}          </View>          {description ? <Text style={[styles.description, ff, { color: muted }]}>{description}</Text> : null}        </View>      ) : null}    </Pressable>  );} const styles = StyleSheet.create({  row: { flexDirection: 'row', alignItems: 'flex-start', gap: 12 },  box: { borderWidth: 2, alignItems: 'center', justifyContent: 'center' },  burst: { position: 'absolute', borderWidth: 2 },  text: { flex: 1, paddingTop: 1 },  labelBox: { alignSelf: 'flex-start' },  label: { fontSize: 15.5, fontWeight: '400' },  strike: { position: 'absolute', left: 0, top: '52%', height: 1.5, borderRadius: 1 },  description: { fontSize: 13, marginTop: 2, lineHeight: 18 },});