Introduction: What React Native Is

React Native draws real iOS and Android views from JavaScript you write; it is not a web page inside a wrapper.

Syntaximport { View, Text, StyleSheet } from "react-native";

React Native is a framework for building iOS and Android apps in JavaScript. You write components in files ending in .tsx. The framework creates real platform views from them: a UIView on iOS, an android.view.View on Android. No browser is involved and nothing runs in a WebView.

It is not HTML and CSS

There is no div and no span. You use View for containers and Text for words, and a string that is not inside a Text throws at runtime. Styles are plain JavaScript objects. They do not cascade, children do not inherit them (the one exception is text nested inside Text), there are no vh or vw units, and flexDirection defaults to column rather than row. Every time something looks like CSS, check it.

What you can build

Most of what is in the app stores: lists, forms, navigation, camera, maps, push notifications, offline storage, animation. Where a platform feature has no JavaScript wrapper you write a small native module, which a later stage covers. Games that redraw a whole scene every frame are a poor fit.

This course

It documents React Native 0.86 with Expo SDK 57, the pairing Expo ships. It begins with a screen running on your own phone and ends with a signed app in both stores, updatable over the air and monitored in production.

The example below renders a centred title with one line of text beneath it, drawn by the platform's own text view.

Example

Example · tsx
import { StyleSheet, Text, View } from "react-native";

// A component is a function that returns a description of what to draw.
// The default export is the component the project renders.
export default function App() {
  return (
    // View is a container. It becomes a real native view, not a div.
    <View style={styles.screen}>
      {/* Every string has to sit inside a Text component. */}
      <Text style={styles.title}>React Native</Text>
      <Text style={styles.body}>
        This text is drawn by the platform's own text view.
      </Text>
    </View>
  );
}

// Styles are plain JavaScript objects, not CSS. They do not cascade and
// nothing inside the View inherits them.
const styles = StyleSheet.create({
  screen: {
    flex: 1, // fill the space the parent gives this view
    alignItems: "center",
    justifyContent: "center",
    // No flexDirection here, so it is column: children stack downwards.
    gap: 8,
    padding: 24,
  },
  title: { fontSize: 24, fontWeight: "600" },
  body: { fontSize: 15, textAlign: "center" },
});

When to use it

  • Decide whether a team that already writes JavaScript can ship iOS and Android without hiring native developers.
  • Share product logic, validation and formatting between a web app and a mobile app in one language.
  • Add a second platform to an app that exists on only one, without writing the whole thing twice.

More examples

The JavaScript you already know is unchanged

Destructuring with defaults, template literals, modules and promises all behave exactly as they do anywhere else. Only the drawing layer is new. Keep logic like this in its own files with no components in them: it is the easiest part of an app to test, and the easiest to share with a web project.

Example · tsx
// A plain module with no UI in it. Nothing here is React Native specific.
export function formatPrice(cents: number, currency: string): string {
  const amount = (cents / 100).toFixed(2);
  return `${currency}${amount}`;
}

export function initials(fullName: string): string {
  const [first = "", last = ""] = fullName.trim().split(" ");
  return `${first.charAt(0)}${last.charAt(0)}`.toUpperCase();
}

console.log(formatPrice(129900, "$"));
console.log(initials("ada lovelace"));

// Output:
// $1299.00
// AL

Two ways to write the same component

Both forms are everywhere in real code, so you need to read both. The arrow form with an implied return is shorter but has nowhere to put a line of setup, so a component usually starts short and becomes a declaration the moment it needs a variable.

Example · tsx
import { Text, View } from "react-native";

type RowProps = { label: string; value: string };

// Longhand: a function declaration, an explicit return, and the
// incoming object read through its name.
export function RowLong(props: RowProps) {
  return (
    <View>
      <Text>
        {props.label}: {props.value}
      </Text>
    </View>
  );
}

// Shorthand: an arrow function, the incoming object destructured in the
// parameter list, and the return implied by the parentheses.
// Same component, same result on screen.
export const RowShort = ({ label, value }: RowProps) => (
  <View>
    <Text>
      {label}: {value}
    </Text>
  </View>
);

Column by default, not row

On screen: a grey block with "first" above "second", then a second grey block with "left" and "right" pushed to opposite ends of one line. This single default accounts for a large share of the confusion that web developers hit in their first hour, so it is worth seeing side by side.

Example · tsx
import { StyleSheet, Text, View } from "react-native";

export default function Layout() {
  return (
    <View style={styles.page}>
      <View style={styles.column}>
        <Text>first</Text>
        <Text>second</Text>
      </View>
      <View style={styles.row}>
        <Text>left</Text>
        <Text>right</Text>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  page: { flex: 1, padding: 16, gap: 24 },
  // No flexDirection given, so children stack downwards.
  column: { backgroundColor: "#eef2f5", padding: 12 },
  // A row is something you ask for here. On the web it is the default.
  row: {
    flexDirection: "row",
    justifyContent: "space-between",
    backgroundColor: "#eef2f5",
    padding: 12,
  },
});

Discussion

  • Be the first to comment on this lesson.