A polished Expo mobile app for browsing, searching, downloading, and reviewing Pakistani board past papers.
Expo SDK 54 | React Native 0.81 | Supabase | TanStack Query
Paper Stack is a mobile-first past-paper library built with Expo Router and Supabase. It helps students browse education boards, choose a class and subject, open real PDF papers, search across published papers, bookmark important material, and keep downloaded files available offline.
The app is designed around fast repeated use: board-based browsing, compact paper cards, subject-aware filtering, offline status handling, and native builds that can exercise Android behavior beyond Expo Go.
- Browse active boards grouped by province from Supabase.
- View board details with board-specific color theming.
- Select class levels and subjects through the board/class/subject flow.
- Fetch live papers from Supabase with board, subject, class, year, and session filters.
- Open PDF papers in the native viewer when available.
- Fall back to an external PDF open action if native preview cannot load.
- Search papers with debounced server-side queries.
- Bookmark papers and hydrate bookmark metadata from the server.
- Download papers for offline reading.
- Restore offline download metadata with local snapshots.
- Continue browsing recently opened papers.
- View common questions grouped by chapter.
- Persist preferences and offline state with Zustand and AsyncStorage.
- Show loading, empty, offline, and network retry states across data-driven screens.
| Area | Technology |
|---|---|
| App runtime | Expo SDK 54, React Native 0.81, React 19 |
| Routing | Expo Router |
| Server data | Supabase direct database access |
| Async state | TanStack Query |
| Local state | Zustand |
| Persistence | AsyncStorage |
| Offline files | Expo FileSystem |
| PDF viewing | react-native-pdf with external-open fallback |
| Styling | NativeWind, Tailwind config, custom theme tokens |
| Icons | Lucide React Native |
| Builds | EAS Build |
paper-stack/
|-- app/
| |-- (tabs)/ # Home, search, downloads, profile
| |-- (stack)/ # Boards, papers, viewer, bookmarks, questions
| |-- _layout.tsx # Root providers, routing, deep links
| |-- onboarding.tsx
| `-- splash.tsx
|-- components/
| |-- browse/ # Board/class/subject/paper browse UI
| |-- common/ # Empty, error, network, skeleton, toast UI
| |-- home/ # Home sections and offline shortcut
| |-- search/ # Search header, filters, results
| |-- viewer/ # PDF viewer, toolbar, download progress
| `-- ui/ # Shared primitives
|-- hooks/
| |-- api/ # TanStack Query hooks
| |-- useDownload.ts
| |-- useBookmark.ts
| `-- useNetworkStatus.ts
|-- lib/
| |-- queries/ # Pure Supabase query functions
| |-- supabase.ts # Supabase client
| |-- query-keys.ts
| `-- offline-files.ts
|-- store/
| `-- index.ts # Zustand store
|-- types/
| `-- index.ts # Server-aligned app types and mappers
|-- constants/ # Theme and offline fallback data
|-- supabase/ # SQL helpers
`-- assets/images/
`-- icon.png
Paper Stack uses Supabase as the primary source of truth. The query layer is split into pure async functions and React hooks:
Supabase tables
-> lib/queries/*
-> hooks/api/*
-> screens/components
The main data entities are:
BoardSubjectPaperCommonQuestionDownloadUserPreferences
Database rows are mapped from snake_case Supabase columns into camelCase mobile types in types/index.ts.
The Supabase client lives in:
lib/supabase.ts
Environment variables are expected in .env for local development:
EXPO_PUBLIC_SUPABASE_URL=
EXPO_PUBLIC_SUPABASE_ANON_KEY=Use .env.example as the template. Do not commit real secrets.
For EAS builds, the same variables must be available to the selected build profile. This project currently defines them under the preview and production profiles in eas.json.
Pure query functions live in lib/queries/:
boards.ts- board list, grouped boards, board detailsubjects.ts- all subjects and board/class subjectspapers.ts- paper lists, paper detail, recent papers, search, hydration by IDsquestions.ts- common question queriessettings.ts- feature flagsanalytics.ts- view/download analytics RPC calls
React hooks live in hooks/api/ and wrap those functions with TanStack Query keys, caching, loading states, and retry behavior.
Downloads are stored as local file records and persisted through Zustand/AsyncStorage. Each download can include a paperSnapshot, so the app can render metadata even when the network is unavailable.
The viewer prefers sources in this order:
- Downloaded local file URI
- Server
pdfUrl - Navigation fallback metadata
The home screen also exposes an offline downloads shortcut when the device is offline.
- Node.js 20+
- npm
- Expo CLI through
npx - Android Studio or a physical Android device for native testing
- Supabase project with the required tables and public read access/RLS policies
npm installCreate .env in the project root:
EXPO_PUBLIC_SUPABASE_URL=https://your-project-ref.supabase.co
EXPO_PUBLIC_SUPABASE_ANON_KEY=your-anon-public-keynpx expo startExpo Go can run most of the app, but native PDF preview is limited. Use a development or preview build for the closest production behavior.
npx tsc --noEmitnpx expo export --platform android --clearBuild preview APK:
eas build -p android --profile previewBuild production Android App Bundle:
eas build -p android --profile productionThe app is configured with:
android.package = com.devjawadsher.paperstack
react-native-pdfis not available inside Expo Go. The app includes a fallback UI for Expo Go and slow native PDF loads.- Supabase credentials used by EAS must be available at build time because Expo public env values are bundled into the app.
constants/boards.tsandconstants/subjects.tsare offline fallback data, not the primary source.constants/questions.tsis deprecated reference data and should not be used by new screens.
- Add real app screenshots to
docs/screenshots/and reference them in this README. - Add a Supabase schema migration set for reproducible setup.
- Add automated smoke tests for the board -> class -> subject -> paper -> viewer flow.
- Add richer analytics dashboards for paper views and downloads.
- Improve in-app PDF rendering diagnostics for malformed or restricted PDF URLs.
Private project. All rights reserved unless a license is added.