Your First Screen

Find the first screen file, change a line, see it on the device, and read what every part of the starter screen does.

Syntaxexport default function Index() { return <View />; }

Every project has one file whose default export is the first screen. In the current default template that is src/app/index.tsx. Many projects keep the same file at app/index.tsx, and a blank template has a single App.tsx at the root. All three play the same role.

Reading the file

A component is a function whose name starts with a capital letter and which returns a description of what to draw. The markup-looking syntax is JSX: <View style={styles.screen}> is a function call written in angle brackets, and braces inside it switch back to ordinary JavaScript. Props are the attributes you pass to a component, and they arrive as one object. View is a container. Text is the only place a string may live. StyleSheet.create at the bottom of the file holds the style objects the screen refers to.

Changing it

Edit the text and save. The device updates in about a second. If nothing happens, press r in the terminal to reload.

What trips people up

Style values are numbers in density-independent points, not pixels and not strings: fontSize: 16, never "16px". Property names are camelCase. A style set on a View reaches nothing inside it. The example renders a centred greeting, a small badge and a hint line.

Example

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

// Props arrive as one object. Destructure them in the parameter list.
type BadgeProps = { label: string };

function Badge({ label }: BadgeProps) {
  return <Text style={styles.badge}>{label}</Text>;
}

// The default export is the component this route renders.
export default function Index() {
  const name = "Fieldnotes";

  return (
    <View style={styles.screen}>
      {/* Braces switch from markup back to JavaScript. */}
      <Text style={styles.title}>Hello, {name}</Text>
      {/* A component with no children is written self-closing. */}
      <Badge label="SDK 57" />
      <Text style={styles.hint}>Change the text and save the file.</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  screen: {
    flex: 1,
    alignItems: "center",
    justifyContent: "center",
    gap: 10,
    padding: 24,
  },
  title: { fontSize: 22, fontWeight: "600" }, // 22 points, not pixels
  badge: { fontSize: 12, letterSpacing: 1, color: "#0a7ea4" },
  hint: { fontSize: 14, color: "#55606a" },
});

When to use it

  • Confirm a new project is wired up by changing one line and watching the phone.
  • Read an unfamiliar screen file and tell markup, style and plain JavaScript apart.
  • Replace the starter screen with the first real screen of your own app.

More examples

The same screen, long form and short form

On screen: two grey rows of text, indistinguishable from each other. Codebases mix both forms freely, so being able to read either matters more than picking one. The short form has nowhere to put a line of setup, so a component written that way usually becomes a declaration the first time it needs a variable.

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

type LineProps = { text: string };

// Longhand: function declaration, explicit return, props read by name.
function LineLong(props: LineProps) {
  return (
    <View style={styles.row}>
      <Text style={styles.text}>{props.text}</Text>
    </View>
  );
}

// Shorthand: arrow function, destructured parameter, implied return.
// The two components are identical in behaviour.
const LineShort = ({ text }: LineProps) => (
  <View style={styles.row}>
    <Text style={styles.text}>{text}</Text>
  </View>
);

export default function Index() {
  return (
    <View style={styles.screen}>
      <LineLong text="written the long way" />
      <LineShort text="written the short way" />
    </View>
  );
}

const styles = StyleSheet.create({
  screen: { flex: 1, justifyContent: "center", gap: 12, padding: 24 },
  row: { backgroundColor: "#eef2f5", padding: 12, borderRadius: 8 },
  text: { fontSize: 15 },
});

The blank template's App.tsx

A project created from the blank template has no routes directory and one App.tsx, which is what most older tutorials show. On screen: one line of centred text on white. If your project looks like this rather than like a src/app directory, everything in this stage still applies; only the file path differs.

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

export default function App() {
  return (
    <View style={styles.container}>
      <Text>Open up App.tsx to start working on your app.</Text>
      {/* Controls the clock and battery row above your screen. */}
      <StatusBar style="auto" />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#fff",
    alignItems: "center",
    justifyContent: "center",
  },
});

No cascade, with one exception

On screen: a small line in the default style, then a larger blue line whose middle word is bold and still blue. Reach for nested Text when part of a sentence needs different weight or colour. Everywhere else, assume a style applies to exactly the one component you put it on.

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

export default function Inheritance() {
  return (
    <View style={styles.wrapper}>
      {/* A View has no text styles at all. fontSize or color on the
          wrapper would not type-check, and would reach nothing. */}
      <Text>plain, styled by nothing</Text>

      {/* Text inside Text does inherit. This is the only inheritance. */}
      <Text style={styles.outer}>
        outer <Text style={styles.inner}>inner</Text> outer again
      </Text>
    </View>
  );
}

const styles = StyleSheet.create({
  wrapper: { flex: 1, justifyContent: "center", gap: 12, padding: 24 },
  outer: { fontSize: 18, color: "#0a7ea4" },
  inner: { fontWeight: "700" }, // keeps the outer size and colour
});

Discussion

  • Be the first to comment on this lesson.