@mariosplen/expo-template-auth
v1.0.1
Published
A template for a React Native Expo app with Supabase authentication and React Query.
Maintainers
Readme
@mariosplen/expo-template-auth
An Expo + expo-router template with Supabase email/password authentication called over plain fetch (no supabase-js dependency), session persistence in expo-secure-store, refresh-on-401 token renewal, and React Query wired up for protected data fetching.
Usage
Scaffold a new project from this template:
npx create-expo-app@latest my-app --template @mariosplen/expo-template-authThen set up your environment variables:
cd my-app
cp .env.local.example .env.localFill in .env.local with your Supabase project's URL and publishable key:
EXPO_PUBLIC_SUPABASE_URL=<your-supabase-url>
EXPO_PUBLIC_SUPABASE_PUBLISHABLE_KEY=<your-supabase-publishable-key>Apply the database schema in supabase/schema.sql to your Supabase project (via the SQL editor or the Supabase CLI), then start the app:
npm install
npm startWhat's included
src/app— expo-router routes, split into an(app)group and an(auth)group that the root layout guards withStack.Protected, plus a+not-foundscreen. The root layout holds the native splash screen until the persisted session has resolved, so logged-in users never see a login-screen flash on cold start.src/lib/auth/— everything that knows about tokensstore.ts— the zustand store holding the session, plussetSession()/loadSession(), which write the store and its persisted copy (SecureStore on native, localStorage on web) together so the two never driftactions.ts— every operation that changes the session (signIn,signUp,signOut,refreshSession), each hitting a GoTrue endpoint and writing the result throughsetSession(). Clears the React Query cache on sign-out so one account's cached data can never leak to the next.request.ts—authorizedRequest(), the only place a data request carries a token. On a 401 it refreshes once and replays the request, so token renewal is driven by the server rejecting a token — no timers, no auth listeners.
src/lib/api.ts— session-agnostic transport shared by every feature:supabaseFetch()(base URL +apikey/Authorizationheaders) andparseJson(), which throws the raw response body as a plainErrorrather than a custom error typesrc/lib/query.ts— the QueryClient the app's provider and sign-out cache clear sharesrc/lib/i18n/— react-i18next with English only;locales/en.jsonholds the strings. Screens translate with theuseTranslation()hook, andt()keys are type-checked againsten.json.src/lib/theme.ts— minimal color tokens so screens don't hardcode hex valuessrc/lib/env.ts— required environment variable validationsupabase/schema.sql— database schema used by the template
Session storage notes
- Native sessions live in the device keychain via
expo-secure-store. SecureStore warns above ~2048 bytes per value and can fail on some Android versions — if your JWTs grow large (many custom claims), switch to theLargeSecureStorepattern from the Supabase docs (AES key in SecureStore, encrypted session in AsyncStorage). - Web sessions persist in
localStorage, so they survive tab closes and are shared across tabs like a normal web app. Tokens inlocalStorageare readable by any script that gets XSS'd into the page — the standard tradeoff for persisting a session in a browser, but worth knowing. - Sign-up with email confirmation enabled (the Supabase default) returns no session and no error — extend the login screen to show a "check your inbox" message if you keep confirmations on.
License
MIT
