EthicsComputerCourses
Join training +91 90532 10052

Styling with StyleSheet

React Native · Lesson 7 of 30 Beginner 6 min read Updated

By the end of this lesson you will be able to style any React Native component, combine and switch styles based on state, add shadows that work on both platforms, and support dark mode. You will also understand why React Native styling feels like CSS but behaves a little differently.

Styles are JavaScript objects

There are no CSS files in React Native. Each component takes a style prop, which is a JavaScript object. The property names are the CSS names written in camelCase: background-color becomes backgroundColor, font-size becomes fontSize.

Sizes have no units. You write padding: 16, not padding: "16px". The number means density-independent pixels, so the size looks similar on every phone. Percentages are written as strings, such as width: "50%".

Step 1: style a fee receipt with StyleSheet.create.

React JSX
import { View, Text, StyleSheet } from "react-native";

export default function Receipt() {
  return (
    <View style={styles.card}>
      <Text style={styles.title}>Fee receipt</Text>
      <Text style={styles.amount}>₹12,500</Text>
      <Text style={styles.note}>Paid by Rohan Gupta, Class 10B</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  card: {
    margin: 16,
    padding: 20,
    borderRadius: 14,
    backgroundColor: "#ffffff",
    borderWidth: 1,
    borderColor: "#dde3ea",
  },
  title: { fontSize: 14, color: "#5b6b7a", textTransform: "uppercase", letterSpacing: 1 },
  amount: { fontSize: 32, fontWeight: "700", marginVertical: 8 },
  note: { fontSize: 14, color: "#5b6b7a" },
});

What you should see: a white card with a thin grey border. A small grey "FEE RECEIPT" label sits above a large bold amount, followed by a grey line of text.

StyleSheet.create is not required (a plain object works), but it is the standard habit. It keeps styles out of your render code, gives you editor autocomplete, and lets the tools warn you about invalid properties.

How React Native styles differ from CSS

It helps to think of CSS as a family rule ("everyone in this house wears blue") and React Native styles as a uniform handed to each person individually.

CSS on the webReact Native
Selectors like .card pNo selectors; each component gets its own style
Styles cascade to childrenOnly nested Text inherits text styles
16px, 1rem, 2emPlain numbers, or percentage strings
display: block by defaultEvery View is a flex container by default
:hover, :activeNo pseudo-classes; use state or Pressable's pressed
Media queriesuseWindowDimensions() in JavaScript

Useful shortcuts that do exist: marginHorizontal and marginVertical (also for padding), StyleSheet.absoluteFill for a view that covers its parent, and StyleSheet.hairlineWidth for the thinnest possible line on the device.

Combining and switching styles

You can pass an array of styles. React Native merges them from left to right, so later styles win. Falsy values such as false or null are ignored, which makes conditional styles easy.

Step 2: a status badge whose colour depends on the order status.

React JSX
import { Text, StyleSheet } from "react-native";

export function StatusBadge({ status }) {
  return (
    <Text
      style={[
        styles.badge,
        status === "delivered" && styles.delivered,
        status === "cancelled" && styles.cancelled,
      ]}
    >
      {status}
    </Text>
  );
}

const styles = StyleSheet.create({
  badge: { paddingHorizontal: 10, paddingVertical: 4, borderRadius: 999, backgroundColor: "#fff3cd", color: "#7a5b00", alignSelf: "flex-start" },
  delivered: { backgroundColor: "#d7f5e3", color: "#1d6b3c" },
  cancelled: { backgroundColor: "#fde2e2", color: "#9b1c1c" },
});

What you should see: <StatusBadge status="pending" /> is a yellow pill, "delivered" is green and "cancelled" is red. The base style gives the shape; the later style only changes the colours.

Try the merge rule yourself

React Native's merge works like spreading objects one after another. This browser example copies that rule with a tiny flatten helper, so you can see which value wins.

React JSXTry it yourself
import { useState } from "react";
import { createRoot } from "react-dom/client";

// Same idea as React Native's style arrays: later objects win, falsy items are skipped
function flatten(styles) {
  return Object.assign({}, ...styles.filter(Boolean));
}

const base = { padding: 12, borderRadius: 8, background: "#eee", color: "#222" };
const selected = { background: "#087ea4", color: "white" };
const disabled = { opacity: 0.4 };

function Chip({ label, isSelected, isDisabled, onToggle }) {
  return (
    <button onClick={onToggle} style={flatten([base, isSelected && selected, isDisabled && disabled])}>
      {label}
    </button>
  );
}

function Filters() {
  const [veg, setVeg] = useState(false);
  return (
    <div style={{ display: "flex", gap: 8 }}>
      <Chip label="Veg only" isSelected={veg} onToggle={() => setVeg(!veg)} />
      <Chip label="Under ₹200" isDisabled />
    </div>
  );
}

console.log(JSON.stringify(flatten([base, selected])));
createRoot(document.getElementById("root")).render(<Filters />);
Output
{"padding":12,"borderRadius":8,"background":"#087ea4","color":"white"}

Click "Veg only" and it turns blue. The padding and radius stay, because selected only replaces the colours.

Shadows on both platforms

Shadows were historically different on each platform. iOS used four shadow* properties, and Android used elevation. With the New Architecture, React Native also supports the web-style boxShadow on both platforms.

React JSX
import { StyleSheet } from "react-native";

const styles = StyleSheet.create({
  // Modern: one property, works on iOS and Android (New Architecture)
  card: { backgroundColor: "white", borderRadius: 12, boxShadow: "0 2px 8px rgba(0, 0, 0, 0.15)" },

  // Older code you will still meet
  oldCard: {
    backgroundColor: "white",
    shadowColor: "#000",
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.15,
    shadowRadius: 8,
    elevation: 4, // Android
  },
});

A shadow needs a background colour on the view, or it may not appear as you expect.

Dark mode and screen sizes

useColorScheme() returns "light" or "dark" from the phone's settings. Keep two colour sets and pick one:

React JSX
import { useColorScheme, useWindowDimensions, View, Text } from "react-native";

const colors = {
  light: { bg: "#ffffff", text: "#111111" },
  dark: { bg: "#121212", text: "#f2f2f2" },
};

export default function Home() {
  const scheme = useColorScheme();
  const theme = colors[scheme === "dark" ? "dark" : "light"];
  const { width } = useWindowDimensions();
  const isTablet = width >= 768;

  return (
    <View style={{ flex: 1, backgroundColor: theme.bg, padding: isTablet ? 48 : 16 }}>
      <Text style={{ color: theme.text, fontSize: isTablet ? 28 : 20 }}>Welcome, Meera</Text>
    </View>
  );
}

What you should see: switch your phone to dark mode and the screen turns dark with light text. On a tablet, the padding and font size are larger. useWindowDimensions updates when the phone rotates, so this replaces CSS media queries.

Common mistakes

  • Adding units. fontSize: "16px" is invalid. Write fontSize: 16.
  • Kebab-case names. "background-color" does nothing. Use backgroundColor.
  • Expecting cascading. Styles on a View do not reach the Text inside it.
  • Percentages as numbers. width: 50 means 50 points. For half the width, write width: "50%".
  • Hard-coded colours everywhere. Keep colours in one theme object so dark mode is a single switch.

Key points

  • Styles are JavaScript objects with camelCase names and unitless numbers.
  • StyleSheet.create organises styles and helps tools catch mistakes.
  • Style arrays merge left to right; falsy items are skipped, which makes conditional styles easy.
  • There are no selectors, cascades or media queries; use state, nested Text and useWindowDimensions.
  • boxShadow works on both platforms with the New Architecture; older code uses shadow* and elevation.
  • useColorScheme() gives you the system light or dark setting.

Exercise

Create a PriceTag component that takes price and onSale props. It shows the price in a rounded box. When onSale is true, the box turns red with white text and shows the word "SALE" before the price. Use one StyleSheet and a style array. Then make the box background dark grey in dark mode when it is not on sale.

Show solution
React JSX
import { Text, StyleSheet, useColorScheme } from "react-native";

export function PriceTag({ price, onSale }) {
  const isDark = useColorScheme() === "dark";
  return (
    <Text style={[styles.tag, isDark && styles.dark, onSale && styles.sale]}>
      {onSale ? "SALE " : ""}₹{price}
    </Text>
  );
}

const styles = StyleSheet.create({
  tag: { alignSelf: "flex-start", paddingHorizontal: 12, paddingVertical: 6, borderRadius: 8, backgroundColor: "#eeeeee", color: "#111111", fontWeight: "600" },
  dark: { backgroundColor: "#333333", color: "#f2f2f2" },
  sale: { backgroundColor: "#c62828", color: "#ffffff" },
});

The sale style comes last in the array, so it wins over both the light and dark colours.

Quick Quiz

1. How do you write a 16-pixel font size in React Native?

2. In style={[styles.a, styles.b]}, both set color. Which one is used?

3. What replaces CSS media queries in React Native?

4. Which hook tells you whether the phone is in dark mode?

Learn React Native with a mentor

Free lessons get you started. Our Mobile App Development training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call