Skip to content

claretnnamocha/rift

Repository files navigation

Rift — Expo React Native Template

Production-ready Expo starter with routing, theming, auth, and a full component library. Break from boilerplate.

Rift is a production-ready Expo React Native template with file-based routing, a full UI component library, auth patterns, and a typed API layer — everything wired up so you can break from boilerplate and start building what matters.


Tech Stack

Layer Library
Framework Expo SDK 54 + Expo Router v6
Language TypeScript (strict)
Styling NativeWind v4 (Tailwind for RN)
State Zustand v5
Server state TanStack React Query v5
Forms react-hook-form + zod
HTTP Axios (with auth interceptors)
Secure storage expo-secure-store / AsyncStorage
Fonts Inter + Geist Mono via expo-google-fonts

Quick Start

# 1. Clone / fork
git clone <repo-url> my-app
cd my-app

# 2. Install dependencies
npm install

# 3. Configure environment
cp .env.example .env
# Edit .env with your values

# 4. Start development server
npm run dev

Environment Variables

Copy .env.example to .env and fill in the values. All client-side env vars must be prefixed with EXPO_PUBLIC_.

Variable Required Description
EXPO_PUBLIC_APP_ENV No development | staging | production (default: development)
EXPO_PUBLIC_API_URL No Backend REST API base URL
EXPO_PUBLIC_SUPABASE_URL No Supabase project URL
EXPO_PUBLIC_SUPABASE_ANON_KEY No Supabase anonymous key

Typed via constants/Config.ts — import Config instead of reading process.env directly.


Folder Structure

.
├── app/                        # Expo Router file-based routes
│   ├── _layout.tsx             # Root layout (fonts, providers, nav)
│   ├── +not-found.tsx
│   ├── form-demo.tsx           # Form patterns demo screen
│   ├── (auth)/                 # Auth group (no tab bar)
│   │   ├── _layout.tsx
│   │   ├── login.tsx
│   │   ├── register.tsx
│   │   └── forgot-password.tsx
│   ├── (tabs)/                 # Tab group (guarded — requires auth)
│   │   ├── _layout.tsx         # Tab bar + auth redirect
│   │   ├── index.tsx           # Home — FlatList with pagination
│   │   ├── explore.tsx         # Component + pattern browser
│   │   └── profile.tsx         # Settings + logout
│   └── item/
│       └── [id].tsx            # Item detail (dynamic route)
│
├── components/
│   ├── ui/                     # Design system components
│   │   ├── Text.tsx
│   │   ├── Button.tsx          # 5 variants, 3 sizes, icons
│   │   ├── TextInput.tsx       # Labels, errors, icons, multiline
│   │   ├── Card.tsx            # elevated / outlined / filled
│   │   ├── Badge.tsx           # 6 variants, dot indicator
│   │   ├── Avatar.tsx          # Image or initials, status dot
│   │   ├── BottomSheet.tsx     # @gorhom + useBottomSheet hook
│   │   ├── Toast.tsx           # ToastProvider + useToast hook
│   │   ├── Modal.tsx           # Slide-up with actions
│   │   ├── Skeleton.tsx        # Shimmer placeholder
│   │   ├── EmptyState.tsx      # Icon + title + CTA
│   │   ├── ErrorBoundary.tsx   # Class component with retry
│   │   ├── Divider.tsx
│   │   └── index.ts            # Barrel export
│   ├── layout/
│   │   ├── Screen.tsx          # Safe area + scroll wrapper
│   │   ├── Stack.tsx           # Flex gap layout primitive
│   │   └── ThemeProvider.tsx   # System/light/dark context
│   └── forms/
│       └── TextInput.tsx       # Re-exports ui/TextInput
│
├── constants/
│   ├── Colors.ts               # Full light + dark color tokens
│   ├── Layout.ts               # Spacing, radius, shadows
│   ├── Typography.ts           # Font scale + families
│   └── Config.ts               # Typed EXPO_PUBLIC_* env vars
│
├── hooks/
│   ├── useAppStore.ts          # Zustand store access
│   ├── useColorScheme.ts       # system/light/dark resolution
│   ├── useThemeColor.ts        # Returns full ThemeColors object
│   └── queries/
│       ├── useItems.ts         # useInfiniteItems, useItem
│       └── useMutateItem.ts    # useUpdateItem, useDeleteItem
│
├── lib/
│   ├── api.ts                  # Axios instance + refresh interceptor
│   ├── queryClient.ts          # TanStack QueryClient singleton
│   ├── storage.ts              # SecureStore + AsyncStorage wrappers
│   ├── mock.ts                 # Mock data + stub API functions
│   └── utils.ts                # Shared utility functions
│
├── store/
│   └── index.ts                # Zustand store (auth + app slices)
│
├── types/
│   └── index.ts                # Shared TypeScript types
│
├── .env                        # Local env (gitignored)
├── .env.example                # Template for env vars
├── app.config.ts               # Expo config with env variants
├── eas.json                    # EAS build profiles
├── tailwind.config.js          # NativeWind config
├── babel.config.js             # Babel with NativeWind preset
├── metro.config.js             # Metro with NativeWind
├── eslint.config.js            # ESLint flat config (typescript + react-hooks)
└── .prettierrc                 # Prettier config

Commands

# Development
npm run dev                  # Start Expo dev server (web + simulator)

# Type checking
npm run typecheck            # tsc --noEmit

# Linting + formatting
npm run lint                 # ESLint with --fix
npm run lint:check           # ESLint read-only
npm run format               # Prettier --write
npm run format:check         # Prettier read-only

# Web build
npm run build:web            # Export static web bundle

# EAS builds
npm run build:dev            # Development build (dev client)
npm run build:preview        # Preview / internal distribution
npm run build:production     # Production build for app stores

# EAS submit
npm run submit:ios           # Submit to App Store Connect
npm run submit:android       # Submit to Google Play

EAS Workflow

Three profiles are configured in eas.json:

Profile Distribution Use case
development Internal Local dev, simulator, dev client
preview Internal Stakeholder testing via TestFlight / internal track
production Store App Store + Google Play submission

First-time setup

# Install EAS CLI
npm install -g eas-cli

# Log in
eas login

# Link project (creates projectId in app.config.ts extra.eas)
eas init

# Build development client
npm run build:dev

Adding a New Screen

  1. Create app/your-screen.tsx (or nested under a group).
  2. Register it in app/_layout.tsx under <Stack> if needed.
  3. Use <Screen> from @/components/layout for safe-area + scroll handling.
  4. Navigate with router.push('/your-screen') or <Link href="/your-screen">.

Forking This Template

# Clone
git clone <repo-url> my-new-app
cd my-new-app

# Remove origin, add your own
git remote remove origin
git remote add origin git@github.com:<you>/<your-repo>.git

# Update package name
# Edit: package.json name, app.config.ts APP_VARIANTS bundle IDs/package names

# Install and start
npm install
cp .env.example .env
npm run dev

Authentication

The template uses mock stubs in lib/mock.ts. To connect to a real auth provider:

  1. Replace mockLogin / mockRegister in the auth screens with real API calls.
  2. The api.ts request interceptor reads the token from StorageKeys.AUTH_TOKEN automatically.
  3. On 401, the response interceptor calls POST /auth/refresh, retries the request, and falls back to logout + redirect.
  4. The unauthorized handler is registered in app/_layout.tsx via setUnauthorizedHandler.

Theming

  • Light / dark / system modes via ThemeMode in the Zustand store.
  • Toggle from Profile screen (cycles light → dark → system).
  • All colors defined in constants/Colors.ts as Colors.light and Colors.dark.
  • Access anywhere with useThemeColor() or useColor('primary').

About

Production-ready Expo starter with routing, theming, auth, and a full component library. Break from boilerplate.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors