npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

expo-supabase-email-confirm

v1.0.1

Published

Add email confirmation with success screen to Expo + Supabase apps

Readme

expo-supabase-email-confirm

Add email confirmation with a success screen to your Expo + Supabase app. Uses direct fetch calls to Supabase Auth API for maximum control and reliability.

The Problem

When users verify their email via deep link:

  1. They get immediately redirected to the dashboard without confirmation
  2. Global auth redirects interfere with showing success screens
  3. Supabase client methods can have timing issues on cold start

The Solution

This package provides:

  • A dedicated confirmation screen that handles the deep link
  • Direct fetch-based auth verification (no Supabase client dependency for critical paths)
  • Support for multiple auth flows (PKCE, implicit, OTP)
  • A success message displayed for a configurable duration
  • Proper error handling with user-friendly messages

Installation

npx expo-supabase-email-confirm

The CLI will prompt you for:

  • App scheme (from your app.json)
  • Success/error message text
  • Loading text
  • Display duration
  • Colors
  • File paths

What Gets Created

  1. pendingAuthUrl.ts - URL handoff utility between root layout and confirm screen
  2. confirm.tsx - The confirmation screen with fetch-based auth

Auth Flows Supported

The generated code handles all Supabase email confirmation flows:

1. PKCE Flow (Recommended)

URL: yourscheme://auth/confirm?code=abc123

Exchanges authorization code for session tokens via:

POST /auth/v1/token?grant_type=pkce

2. Implicit Flow

URL: yourscheme://auth/confirm#access_token=...&refresh_token=...

Tokens are in the URL fragment and set directly.

3. OTP/Token Hash Flow

URL: yourscheme://auth/confirm?token_hash=...&type=signup

Verifies the token via:

POST /auth/v1/verify

Manual Setup Required

After running the CLI, you need to:

1. Update app/_layout.tsx

Add the URL handoff in your deep link handler:

import { setPendingAuthUrl } from '@/utils/pendingAuthUrl';

// In your deep link handling useEffect:
if (url.includes('auth/confirm')) {
  setPendingAuthUrl(url);
  return; // Don't process here - let confirm screen handle it
}

2. Bypass Auth Redirect for Confirm Screen

In your auth redirect hook, add an early return:

const onConfirmScreen = segments[0] === 'auth' && segments[1] === 'confirm';

// Let confirm screen handle its own navigation
if (onConfirmScreen) {
  return;
}

3. Add Route to Stack

<Stack.Screen name="auth/confirm" options={{ animation: 'none' }} />

4. Configure Supabase

In Supabase Dashboard → Authentication → URL Configuration:

Redirect URLs: yourscheme://auth/confirm

5. Implement Auth Store Integration

The generated confirm.tsx has placeholder functions you need to implement:

// getCurrentSession() - Check if session already exists
// setSession(accessToken, refreshToken) - Store tokens in your auth system
// isAuthStoreReady(userId) - Check if auth store has user loaded

Example with Supabase client:

import { supabase } from '@/services/supabase';
import { useAuthStore } from '@/store';

async function getCurrentSession() {
  const { data: { session } } = await supabase.auth.getSession();
  return session;
}

async function setSession(accessToken: string, refreshToken: string) {
  const { error } = await supabase.auth.setSession({
    access_token: accessToken,
    refresh_token: refreshToken,
  });
  return !error;
}

function isAuthStoreReady(userId: string) {
  return useAuthStore.getState().user?.id === userId;
}

How It Works

┌─────────────────────────────────────────────────────────────┐
│ 1. User taps email confirmation link                        │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│ 2. App opens via deep link                                  │
│    yourscheme://auth/confirm#access_token=...               │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│ 3. _layout.tsx detects auth/confirm URL                     │
│    → Stores URL via setPendingAuthUrl()                     │
│    → Does NOT process tokens (avoids duplicate sessions)    │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│ 4. confirm.tsx retrieves URL via getPendingAuthUrl()        │
│    → Parses tokens from URL fragment/query                  │
│    → Calls Supabase Auth API directly via fetch             │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│ 5. Session established                                      │
│    → Tokens stored via setSession()                         │
│    → Success screen shown for 2.5 seconds                   │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│ 6. Navigate to dashboard                                    │
│    → No skeleton overlay (doesn't call setTransitioning)    │
└─────────────────────────────────────────────────────────────┘

Key Implementation Details

  • URL Handoff Pattern: Uses module-level state to pass URL from root layout to confirm screen, preventing duplicate token processing
  • Direct Fetch Calls: Uses fetch() to call Supabase Auth API instead of client methods for more control and better error handling
  • No Skeleton Overlay: Doesn't trigger setTransitioning() to avoid covering the success screen
  • Minimum Display Time: Ensures success message is visible for the configured duration before navigation
  • Auth Store Sync: Waits for auth store to update before navigating to prevent flicker

Dependencies

This package assumes you have:

  • expo-router
  • expo-linking
  • react-native-reanimated (optional, for animations)
  • react-native-safe-area-context

Note: This package does NOT require @supabase/supabase-js for the confirmation flow itself - it uses direct fetch calls. However, you'll likely have it installed for other auth operations.

Environment Variables

The generated code expects these environment variables:

EXPO_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
EXPO_PUBLIC_SUPABASE_ANON_KEY=your-anon-key

License

MIT