Skip to content

Repository files navigation

Expo App Scaffold

A production-ready scaffold for building iOS applications with Expo, React Native, and Supabase. Designed to be easily renamed and customized for new projects.

Tech Stack

  • Frontend: Expo SDK 54 + React Native 0.81 + TypeScript
  • Styling: NativeWind v4 (Tailwind CSS for React Native)
  • State Management: React Query with offline persistence
  • Backend: Supabase (Auth, Database, Storage) + Express API on AWS Lambda
  • Navigation: Expo Router (file-based routing)
  • Build: EAS Build with three environments (dev, preview, prod)
  • Testing: Jest + React Native Testing Library

Quick Start

Prerequisites

  • Node.js 18+
  • npm or yarn
  • Expo CLI (npm install -g expo-cli)
  • EAS CLI (npm install -g eas-cli)
  • Supabase account
  • AWS account (for backend deployment)

Installation

# Clone the repository
git clone <your-repo-url>
cd expo-app-scaffold

# Install dependencies
make install

# Set up environment variables
cp .env.example .env
# Edit .env with your values

Customizing for Your App

  1. Update .env with your app name, bundle ID, and credentials:

    EXPO_PUBLIC_APP_NAME="Your App Name"
    EXPO_PUBLIC_APP_SLUG="your-app-slug"
    EXPO_PUBLIC_BUNDLE_ID="com.yourcompany.yourapp"
  2. Initialize EAS (after creating an Expo account):

    eas init
    # Add the project ID to .env as EXPO_PUBLIC_EAS_PROJECT_ID
  3. Add your assets in assets/branding/:

    • app-icon.png (1024x1024)
    • splash.png (1284x2778)

Development

# Start the Expo development server
make dev

# Start the backend server
make dev-backend

# Start iOS simulator
make ios

# Start Android emulator
make android

Project Structure

expo-app-scaffold/
├── app/                      # Expo Router screens
│   ├── (auth)/              # Authentication screens
│   ├── (tabs)/              # Main app tabs
│   ├── _layout.tsx          # Root layout with providers
│   └── index.tsx            # Entry point
│
├── lib/                      # Shared library code
│   ├── api/                 # HTTP client with interceptors
│   │   └── client.ts
│   │
│   ├── components/
│   │   ├── ErrorBoundary.tsx # Error boundary for crash recovery
│   │   └── ui/              # Reusable UI components
│   │
│   ├── constants/           # Design system tokens
│   │   ├── colors.ts
│   │   ├── typography.ts
│   │   ├── spacing.ts
│   │   ├── shadows.ts
│   │   └── animations.ts
│   │
│   ├── hooks/               # Custom React hooks
│   │
│   ├── providers/           # React context providers
│   │   ├── AuthProvider.tsx
│   │   ├── AlertProvider.tsx
│   │   └── QueryProvider.tsx
│   │
│   ├── services/            # External service clients
│   │   ├── supabase.ts
│   │   └── logger.ts
│   │
│   ├── query/               # React Query configuration
│   │   └── keys.ts          # Query key factory
│   │
│   ├── types/               # TypeScript type definitions
│   │   ├── common.ts
│   │   └── database.ts      # Auto-generated from Supabase
│   │
│   └── utils/               # Utility functions
│       ├── storage.ts       # AsyncStorage + SecureStore wrapper
│       ├── date.ts          # Date formatting helpers
│       └── validation.ts    # Zod validation schemas
│
├── backend/                  # Backend API
│   ├── src/
│   │   ├── index.ts         # Express entry point
│   │   ├── middleware/      # Express middleware
│   │   └── routes/          # API routes
│   ├── serverless.yml       # AWS Lambda config
│   └── package.json
│
├── __tests__/               # Test files
│   ├── components/
│   ├── utils/
│   └── test-utils/          # Test utilities
│
├── app.config.js            # Expo configuration
├── babel.config.js          # Babel configuration
├── metro.config.js          # Metro bundler configuration
├── tailwind.config.js       # NativeWind configuration
├── tsconfig.json            # TypeScript configuration
├── jest.config.js           # Jest configuration
├── eas.json                 # EAS Build configuration
└── Makefile                 # Build automation

Features

Error Handling

The app includes an ErrorBoundary component that catches JavaScript errors:

// Automatically wrapped in root layout
// Custom error handling in app/_layout.tsx

API Client

HTTP client with automatic token handling, retry logic, and timeout:

import { apiClient } from '@/lib/api';

// Automatic auth headers and retry
const { data } = await apiClient.get('/api/users');
await apiClient.post('/api/users', { name: 'John' });

Validation

Form validation with Zod:

import { validate, loginSchema } from '@/lib/utils/validation';

const result = validate(loginSchema, { email, password });
if (!result.success) {
  console.log(result.errors);
}

Storage

Type-safe storage utilities:

import { storage, secureStorage, STORAGE_KEYS } from '@/lib/utils/storage';

// Regular storage
await storage.set('user', userData);
const user = await storage.get<User>('user');

// Secure storage for sensitive data
await secureStorage.set(STORAGE_KEYS.AUTH_TOKEN, token);

Date Formatting

import { formatDate, formatRelative, isToday } from '@/lib/utils/date';

formatDate(new Date(), 'short');    // "Jan 15, 2024"
formatRelative(pastDate);           // "2 hours ago"
isToday(date);                      // true/false

Logging

Environment-aware logging:

import { logger } from '@/lib/services/logger';

logger.debug('Debug info');   // Only in development
logger.info('User action');   // Development + preview
logger.error('Error!', { details });  // All environments

Design System

Colors

import { COLORS } from '@/lib/constants/colors';

COLORS.primary      // Main brand color
COLORS.error        // Error states
COLORS.text         // Primary text
COLORS.border       // Borders

Typography

import { Typography } from '@/lib/components/ui';

<Typography variant="h1">Heading</Typography>
<Typography variant="body" color="secondary">Text</Typography>

Spacing

import { SPACING } from '@/lib/constants/spacing';

SPACING.xs   // 4
SPACING.sm   // 8
SPACING.md   // 12
SPACING.lg   // 16

Testing

# Run all tests
make test

# Run with coverage
make test-coverage

# Run in watch mode
make test-watch

Test utilities are provided in __tests__/test-utils/:

import { render, screen } from '@/__tests__/test-utils/render';

render(<MyComponent />);
expect(screen.getByText('Hello')).toBeTruthy();

Building & Deployment

Development Builds

make build-dev          # All platforms
make build-ios-dev      # iOS only
make build-android-dev  # Android only

Preview Builds (TestFlight)

make build-preview

Production Builds

make build-prod

Backend Deployment

make deploy-backend-dev   # Development
make deploy-backend-prod  # Production

App Store Submission

make submit-ios      # App Store Connect
make submit-android  # Play Console

Environment Variables

Variable Description
EXPO_PUBLIC_APP_NAME App display name
EXPO_PUBLIC_APP_SLUG App slug (lowercase, no spaces)
EXPO_PUBLIC_BUNDLE_ID Bundle identifier
EXPO_PUBLIC_EAS_PROJECT_ID EAS project ID
EXPO_PUBLIC_SUPABASE_URL Supabase project URL
EXPO_PUBLIC_SUPABASE_ANON_KEY Supabase anonymous key
EXPO_PUBLIC_API_URL Backend API URL

Best Practices

  1. Use the Design System - Never hardcode colors, spacing, or typography
  2. Query Key Factory - Use queryKeys for consistent cache management
  3. Error Handling - Use useAlert() for user feedback
  4. Loading States - Use <Skeleton /> components
  5. Form Validation - Use Zod schemas from lib/utils/validation
  6. Type Safety - Generate database types with make db-generate-types

License

MIT

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages