The Project, File by File

Which files in a new Expo project you edit, which are generated, and what app.json, the routes directory and ios and android are for.

Syntaxapp.json: { "expo": { "name": "...", "slug": "...", "icon": "...", "plugins": [] } }

A new Expo project has about a dozen entries at the top level. Roughly half are yours to edit; the rest are generated and can be deleted at any time.

Files you edit

PathWhat it is
app.json or app.config.tsName, slug, icon, splash screen, bundle identifier, plugins. The input to the native projects.
package.jsonDependencies and scripts. Add packages with npx expo install so versions match the SDK.
app/ or src/app/Route files. A file here becomes a screen; Stage 4 covers how that works.
components/, assets/Your own components; images and fonts.
tsconfig.jsonCompiler settings, including any import alias the template configured.

Files you do not

node_modules/ and .expo/ are caches. ios/ and android/ appear when you run npx expo prebuild or a local build, and are generated from your app config, which is why a new project adds them to .gitignore.

What trips people up

Editing app.json does not reach the device through a save. The icon, the display name, permission strings and plugin changes all need a new native build before you can see them.

Example

Example · tsx
// Project file: app/index.tsx
// A file in the routes directory whose default export is the screen.
import { Image, StyleSheet, Text, View } from "react-native";

export default function Index() {
  return (
    <View style={styles.screen}>
      {/* assets/ holds images and fonts. require resolves the path at
          build time, so a typo fails the bundle rather than the screen.
          ../ leaves the routes directory for the project root. */}
      <Image
        source={require("../assets/images/icon.png")}
        style={styles.icon}
      />
      <Text style={styles.title}>Fieldnotes</Text>
      <Text style={styles.hint}>
        The name and this icon are both set in app.json.
      </Text>
    </View>
  );
}

const styles = StyleSheet.create({
  screen: {
    flex: 1,
    alignItems: "center",
    justifyContent: "center",
    gap: 10,
    padding: 24,
  },
  icon: { width: 64, height: 64, borderRadius: 12 },
  title: { fontSize: 20, fontWeight: "600" },
  hint: { fontSize: 13, color: "#55606a", textAlign: "center" },
});

When to use it

  • Find where the display name and icon are set before making a first build.
  • Decide what belongs in version control when a teammate's checkout has native folders and yours does not.
  • Add an image or a font and know which directory and which config file it touches.

More examples

app.json, the file the native projects come from

Everything here ends up inside the generated native projects: name becomes the label under the home screen icon, bundleIdentifier and package are the identities the stores recognise, and plugins lists the libraries allowed to modify the native build. Treat this file as source code, because a change to it means a rebuild rather than a reload.

Example · json
{
  "expo": {
    "name": "Fieldnotes",
    "slug": "fieldnotes",
    "version": "1.0.0",
    "orientation": "portrait",
    "scheme": "fieldnotes",
    "userInterfaceStyle": "automatic",
    "icon": "./assets/images/icon.png",
    "ios": {
      "bundleIdentifier": "com.example.fieldnotes",
      "supportsTablet": true
    },
    "android": {
      "package": "com.example.fieldnotes",
      "adaptiveIcon": {
        "foregroundImage": "./assets/images/adaptive-icon.png",
        "backgroundColor": "#ffffff"
      }
    },
    "plugins": ["expo-router"]
  }
}

package.json: scripts, entry point, pinned SDK

In a routes-based project main points at the router's entry rather than at a file of yours, which is why there is no App.tsx to find. The react and react-native versions are chosen by the SDK, not by you: install with npx expo install so each package resolves to the version Expo SDK 57 was tested against.

Example · json
{
  "name": "fieldnotes",
  "main": "expo-router/entry",
  "scripts": {
    "start": "expo start",
    "android": "expo run:android",
    "ios": "expo run:ios",
    "lint": "expo lint"
  },
  "dependencies": {
    "expo": "~57.0.0",
    "react": "19.2.3",
    "react-native": "0.86.0"
  }
}

Generated, ignored, regenerated

Run prebuild --clean when a native-side change refuses to take effect, or after changing plugins or identifiers. Because the folders are generated, deleting them is a safe first move when a local build fails for no visible reason.

Example · bash
# Added to .gitignore by create-expo-app. None of this is source.
#   node_modules/
#   .expo/
#   ios/
#   android/

# Rebuild the native projects from app.json and your plugins
npx expo prebuild --clean

# Add a dependency at the version this SDK expects
npx expo install expo-image

# Check that nothing in the project conflicts with the installed SDK
npx expo-doctor

Discussion

  • Be the first to comment on this lesson.