Convex

PreviousNext

A React Native starter with BNA UI components and a Convex backend — real-time data, with or without Google, Apple, password and email OTP authentication.

Convex is a real-time backend. You write queries and mutations as TypeScript functions, and every client subscribed to a query re-renders when its data changes — no refetching, no cache invalidation.

BNA UI ships two Convex scaffolds: one with a backend and no sign-in, one with @convex-dev/auth and the sign-in screens already built.

This section covers the schema, authentication, realtime, storage, actions and deployment for both scaffolds, plus provider setup for Google, Apple and Resend.

The auth starter

Built around the same principles as the rest of BNA UI:

  • Open Code: the authentication screens and the Convex functions are copied into your project, not imported from a package.
  • Mobile-First: flows designed for React Native, with tokens in the platform keychain.
  • Cross-Platform: iOS, Android and web.
  • Real-Time: every query is a live subscription.

Sign-in methods

MethodProviderShips with a button
Anonymous@convex-dev/auth AnonymousYes
Email + password@convex-dev/auth Password, with resetYes
Email OTPResendYes
Google@auth/core GoogleYes
Apple@auth/core AppleYes
GitHub@auth/core GitHubNo — configured only

GitHub is set up in convex/auth.ts but the sign-in screen has no button for it. Model one on components/auth/google.tsx if you want it.

What lands in your project

app/_layout.tsx           ConvexAuthProvider, switching on auth state
components/auth/
├── auth.tsx              The sign-in screen: Password / OAuth / OTP tabs
├── password.tsx          Sign in, sign up, forgot and reset password
├── email-otp.tsx         Passwordless email codes
├── google.tsx            Google sign-in button
├── apple.tsx             Apple sign-in button
└── singout.tsx           Sign-out button, used in the settings tab
convex/
├── auth.ts               Providers, password rules, redirect allow-list
├── auth.config.ts        JWT issuer
├── schema.ts             authTables + a users table
├── users.ts              User queries and mutations
├── http.ts               Mounts the auth HTTP routes
├── resendOTP.ts          Email OTP delivery
├── passwordReset.ts      Password-reset codes
└── resendPasswordOTP.ts

The root layout renders a spinner while the session resolves, the sign-in screen when signed out, and your tabs when signed in:

app/_layout.tsx
<ConvexAuthProvider client={convex} storage={secureStorage}>
  <AuthLoading>
    <Spinner size='lg' variant='circle' />
  </AuthLoading>
  <Unauthenticated>
    <Auth />
  </Unauthenticated>
  <Authenticated>
    <Stack>{/* your app */}</Stack>
  </Authenticated>
</ConvexAuthProvider>

storage is expo-secure-store on iOS and Android — the Keychain and the Keystore respectively — and the platform default on web.

Reading the signed-in user

convex/users.ts
export const get = query({
  handler: async (ctx) => {
    const authId = await getAuthUserId(ctx);
 
    if (!authId) {
      throw new Error('Not authenticated');
    }
 
    return await ctx.db.get(authId);
  },
});

Password rules

convex/auth.ts requires at least 8 characters with one digit, one lowercase and one uppercase letter. Edit validatePasswordRequirements to change it — it throws, and the message surfaces in the sign-up form.

Environment

VariableWhere it livesSet byUsed for
EXPO_PUBLIC_CONVEX_URL.env.localnpx convex devBuilding the client
EXPO_URLConvex deploymentbna-ui convexAllow-listing your app scheme
SITE_URLConvex deploymentbna-ui convexAllow-listing the web redirect target
AUTH_RESEND_KEYConvex deploymentyouEmail OTP and password reset
AUTH_GOOGLE_ID/SECRETConvex deploymentyouGoogle sign-in
AUTH_APPLE_ID/SECRETConvex deploymentyouApple sign-in
CONVEX_SITE_URLConvex deploymentConvex, automaticallyThe JWT issuer

Deployment variables are set with npx convex env set NAME value and listed with npx convex env list. They are not in .env.local, because server-side functions read them.

The full walkthrough, including production, is in the auth installation guide.

Guides

Learn more