JSX

JSX is markup written inside JavaScript that compiles to function calls, and its tags are native views rather than HTML.

Syntaxconst element = <View style={styles.box}><Text>{name}</Text></View>;

JSX is a syntax extension to JavaScript. It lets you write an element tree in the middle of a function instead of building it by hand with nested calls. React Native uses it for the same reason React does on the web: a screen reads better as a tree.

It is a function call underneath

A build step rewrites every tag. <Text>Hi</Text> becomes a call to jsx() from react/jsx-runtime, which returns a plain object describing the element. The older classic transform produced React.createElement(Text, null, "Hi") instead, which is why old files begin with import React from "react". Expo's Babel preset uses the automatic runtime, so that import is not needed. The two forms describe the same element.

Braces are a door back into JavaScript

Inside JSX, {} evaluates an expression and inserts the result: {user.name}, {count + 1}, a template literal, a ternary. Expressions only. if and for are statements and will not go there, so do that work above the return. A prop takes any value, so style={{ padding: 8 }} is an object literal inside an expression. That is why there are two braces, not because of CSS.

There is no HTML here

These tags are not HTML elements. <View> is not a div; it is a component that maps to the real native view on each platform. There is no span, no p, no cascade. Every string must sit inside a <Text>.

Example

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

export default function App() {
  const name = "Ada";
  const hours = 5;

  return (
    <View style={styles.screen}>
      {/* Braces switch from JSX back into JavaScript. Any expression works. */}
      <Text style={styles.title}>Hello, {name}</Text>

      {/* A ternary is an expression, so it is allowed here.
          An if statement is not, and would be a syntax error. */}
      <Text>{hours === 1 ? "1 hour left" : `${hours} hours left`}</Text>

      {/* <View /> self-closed is the same as <View></View> with no children. */}
      <View style={styles.rule} />
    </View>
  );
}

const styles = StyleSheet.create({
  screen: { flex: 1, justifyContent: "center", paddingHorizontal: 24 },
  title: { fontSize: 24, fontWeight: "600", marginBottom: 8 },
  rule: { height: 1, backgroundColor: "#CCCCCC", marginTop: 16 },
});

// On screen: "Hello, Ada" in large type, "5 hours left" beneath it, and a
// one-pixel grey line under both, all centred vertically on the phone.

When to use it

  • Read someone else's screen file and tell which parts are markup and which are plain JavaScript
  • Insert a computed value, such as a formatted price, into a label
  • Recognise React.createElement in an older file as the same thing your JSX produces

More examples

The same element, written two ways

Two lines reading "Hi" appear, one from each form, because both produce the identical element object. Worth writing out once so the compiled output is not a mystery, and so that a file using createElement is recognisable as ordinary JSX in disguise. Note that styles is read inside the component, not at module top level, because const declarations are not available before they are evaluated.

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

export default function TwoWays() {
  // Shorthand: JSX. The build step turns this into a function call.
  const withJsx = <Text style={styles.line}>Hi</Text>;

  // Longhand: the call, written by hand.
  // createElement(type, props, ...children) returns the same element object.
  // Since React 17 the automatic runtime emits jsx() from "react/jsx-runtime"
  // rather than createElement, but createElement still works and still builds
  // an equivalent element.
  const withoutJsx = createElement(Text, { style: styles.line }, "Hi");

  return (
    <View style={styles.screen}>
      {withJsx}
      {withoutJsx}
    </View>
  );
}

const styles = StyleSheet.create({
  screen: { flex: 1, paddingTop: 64, alignItems: "center" },
  line: { fontSize: 16 },
});

Expressions, not statements

The screen shows "7.3 average" with "good" under it. This is the shape almost every real component takes: a few lines of calculation, then a return that is nothing but structure. Reach for it whenever you catch yourself trying to put a loop or an if inside braces.

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

const scores = [8, 5, 9];

export default function Average() {
  // Anything that needs a statement happens above the return, in plain
  // JavaScript. JSX only takes the finished value.
  const total = scores.reduce((sum, n) => sum + n, 0);
  const average = (total / scores.length).toFixed(1);
  const verdict = Number(average) >= 7 ? "good" : "needs work";

  return (
    <View style={styles.screen}>
      {/* A template literal is an expression, so it belongs in braces. */}
      <Text style={styles.big}>{`${average} average`}</Text>
      <Text>{verdict}</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  screen: { flex: 1, justifyContent: "center", alignItems: "center" },
  big: { fontSize: 32, fontWeight: "700" },
});

A prop is just a JavaScript value

A pale blue block with the word "Tile" in it, in blue bold type. It shows the two things that confuse people about the double braces and the style array: both are plain JavaScript, not CSS syntax. Use the array form when a base style is combined with a conditional one.

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

export default function Tile() {
  const highlighted = true;

  return (
    <View
      // The outer braces are the JSX expression; the inner braces are an
      // object literal. This is not a CSS declaration block, and the keys
      // are camelCase JavaScript properties.
      style={{ padding: 16, backgroundColor: highlighted ? "#E6F4F9" : "#FFFFFF" }}
    >
      {/* An array of styles is merged left to right; the later one wins.
          A false or null entry in the array is ignored. */}
      <Text style={[styles.label, highlighted && styles.labelOn]}>Tile</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  label: { fontSize: 16, color: "#333333" },
  labelOn: { color: "#0A7EA4", fontWeight: "600" },
});

Discussion

  • Be the first to comment on this lesson.