Shipnative
ShipNativeShipNative
Sign inStart building
GuideOctober 2026 · 7 min read

React Native Switch: Colors, Size, and Settings Rows

Switch is the smallest component in React Native core and still manages to catch people out three ways: it flips back after you tap it, its off colour ignores you on iOS, and it refuses to be resized. None of these are bugs. They follow from how the component is designed, and once you know the rules a switch takes about ten lines. This guide covers those rules, a full settings row that is accessible and saves optimistically, and when a switch is the wrong control.

Rule one: it is controlled

import { useState } from 'react';
import { Switch } from 'react-native';

export default function NotificationsToggle() {
  const [enabled, setEnabled] = useState(false);

  return (
    <Switch
      value={enabled}
      onValueChange={setEnabled}
      trackColor={{ false: '#3a3a3a', true: '#fb923c' }}
      thumbColor="#ffffff"
      ios_backgroundColor="#3a3a3a"
    />
  );
}

The switch shows whatever value says. Tapping it calls onValueChange with the new boolean and nothing more. Forget to store that boolean, or store it somewhere the switch does not read, and the knob slides across and slides straight back. That snap-back is the single most searched Switch problem, and the fix is always the same: make sure value comes from the state that onValueChange updates.

Rule two: colours work differently per platform

PropiOSAndroid
valueRequired to show stateRequired to show state
trackColor.trueTrack colour when onTrack colour when on
trackColor.falseOutline only when offTrack colour when off
ios_backgroundColorFill colour when offIgnored
thumbColorKnob colourKnob colour
width / heightIgnoredIgnored

The row that trips people up is trackColor.false. On Android it fills the track when the switch is off. On iOS it only tints the thin border, so a dark-mode app that sets it and nothing else gets a pale grey iOS switch sitting on a black screen. Set ios_backgroundColor to the same colour and both platforms match. Even then the two switches will not look identical, because each platform draws its own control. If pixel-identical matters, you are building a custom toggle.

Rule three: size is fixed

width and height on a Switch do nothing. The only lever is a transform:

<Switch
  value={enabled}
  onValueChange={setEnabled}
  style={{ transform: [{ scale: 0.8 }], marginRight: -6 }}
/>

A transform changes how the switch is painted, not how much space it takes. The layout still reserves the full-size box, so a shrunk switch leaves a gap and an enlarged one overlaps whatever sits next to it. The negative margin above pulls the row edge back in. Keep scaling modest. Below about 0.75 the touch target gets too small to hit reliably.

A real settings row: tappable label, accessible, optimistic

In a settings screen, users tap the words, not the tiny switch. The whole row should toggle, screen readers should hear one control with a name and a state, and the switch should move instantly even though the save goes to a server.

import { useState } from 'react';
import { Alert, Pressable, StyleSheet, Switch, Text, View } from 'react-native';

export function SettingRow({ label, hint, initial, save }) {
  const [on, setOn] = useState(initial);

  const toggle = async (next: boolean) => {
    setOn(next);                       // move the switch now
    try {
      await save(next);
    } catch {
      setOn(!next);                    // put it back if the save failed
      Alert.alert('Could not save', 'Check your connection and try again.');
    }
  };

  return (
    <Pressable
      onPress={() => toggle(!on)}
      accessibilityRole="switch"
      accessibilityLabel={label}
      accessibilityHint={hint}
      accessibilityState={{ checked: on }}
      style={styles.row}
    >
      <View style={styles.text}>
        <Text style={styles.label}>{label}</Text>
        {hint ? <Text style={styles.hint}>{hint}</Text> : null}
      </View>
      <Switch
        value={on}
        onValueChange={toggle}
        importantForAccessibility="no-hide-descendants"
        accessibilityElementsHidden
        trackColor={{ false: '#3a3a3a', true: '#fb923c' }}
        ios_backgroundColor="#3a3a3a"
        thumbColor="#ffffff"
      />
    </Pressable>
  );
}

const styles = StyleSheet.create({
  row: { flexDirection: 'row', alignItems: 'center', paddingVertical: 14, paddingHorizontal: 16 },
  text: { flex: 1, paddingRight: 12 },
  label: { color: '#fff', fontSize: 16 },
  hint: { color: '#8a8a8a', fontSize: 13, marginTop: 2 },
});

The row carries the switch role and the checked state, and the inner Switch is hidden from the screen reader, so VoiceOver and TalkBack announce one control instead of a label and a separate unlabelled toggle. The optimistic update means the switch never feels slow, and the rollback means it never lies about what was saved. If the setting is stored on the device instead, the same row works with a synchronous save to MMKV or AsyncStorage.

When not to use a switch

A switch promises that the change has already happened. That makes it right for settings screens and wrong inside forms with a Save or Submit button, where it leaves people unsure whether their choice is live yet. For “choose now, apply later”, use a checkbox. For choices with more than two states, such as light, dark, and system theme, use a segmented control or a short list, not two switches that can contradict each other.

Settings screens are the kind of screen that looks finished in a design file and turns out to be wrong in the hand. Describe yours, “a settings screen with toggles for notifications, weekly email, and haptics”, and ShipNative builds it as real React Native that runs on your phone, so you can tap the rows and hear them with VoiceOver before shipping.

Frequently Asked Questions

Why does my React Native Switch flip back after I tap it?

Switch is a controlled component. Tapping it calls onValueChange with the new value but does not change what is shown; the value prop decides that. If you never update the state you pass as value, the switch animates and then returns to where it was.

How do I change the colour of a Switch when it is off on iOS?

Use ios_backgroundColor. On iOS, trackColor.false only colours the thin outline of the off state, so setting it alone looks like it did nothing. trackColor.true sets the on colour on both platforms, and thumbColor sets the knob.

How do I make a Switch bigger or smaller?

Switch ignores width and height. Use transform: [{ scale: 0.8 }] or similar, but remember the layout box keeps the original size, so a scaled-down switch leaves empty space around it and a scaled-up one overlaps its neighbours. Compensate with margins, or build a custom toggle if you need a very different size.

Should I use a Switch or a checkbox?

A switch means the change takes effect immediately, like turning on notifications. A checkbox means the choice is collected and applied later, like agreeing to terms before pressing Sign up. Using a switch inside a form that needs a Save button confuses people about whether their change already happened.

Does Switch work in Expo Go?

Yes. Switch is part of React Native core, so there is nothing to install and it works in Expo Go, development builds, and production apps the same way.

Can an AI app builder generate a settings screen with switches?

Yes. Describe it, "a settings screen with toggles for push notifications, weekly email, and dark mode, saved to the user profile", and ShipNative generates the rows, the switches, and the save logic as real React Native, then runs it on your phone.

→

React Native Checkbox

The control to use when the choice is applied later, not now.

Read guide →
→

React Native Dark Mode

The most common switch in any settings screen, wired up properly.

Read guide →

Ship a real React Native app today

Describe, preview, and export Expo code, free to start.

Build with ShipNative
ShipNative

Build mobile apps with AI. Describe it, try it on your phone, own the code.

Features

Text to App AIApp Generator from ScreenshotPRD to Mobile App

Tools

All free toolsApp Cost CalculatorApp Name GeneratorApp Store Keyword ToolReact Native Components

Blog

All blog postsHow to Build an App Without CodingBest AI Tools for Real Mobile AppsExpo EAS App Store ChecklistLovable, Cursor & v0 for MobileBest AI App Builders in 2026React Native AI App Builder Guide
© 2026 ShipNative
PrivacyTermsFAQPricingDiscord