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

@passkeyme/react-auth

v2.3.3

Published

React integration for Passkeyme Authentication SDK - Simple single dependency installation

Readme

@passkeyme/react-auth

React integration for Passkeyme Authentication SDK. Build secure authentication into your React apps with hooks and components.

🚀 Features

  • 🪝 React Hooks: usePasskeyme(), useAuth(), useAuthState()
  • 🧩 Pre-built Components: Login buttons, user profiles, protected routes
  • 🔄 Automatic State Management: Context-based state with automatic updates
  • 🎨 Customizable UI: Styled components with full customization options
  • 🛡️ Type Safety: Full TypeScript support
  • ⚡ Zero Config: Works out of the box with sensible defaults

📦 Installation

npm install @passkeyme/auth @passkeyme/react-auth

Peer Dependencies:

  • React >= 16.8.0
  • React DOM >= 16.8.0

🔧 Quick Start

1. Setup Provider

Wrap your app with the PasskeymeProvider:

import { PasskeymeProvider } from "@passkeyme/react-auth";

function App() {
  return (
    <PasskeymeProvider
      config={{
        appId: "your-passkeyme-app-id",
        redirectUri: "http://localhost/auth/callback",
        debug: process.env.NODE_ENV === "development", // Enable debug logging in development
      }}
    >
      <YourApp />
    </PasskeymeProvider>
  );
}

2. Use Authentication Hooks

import { usePasskeyme, PasskeymeCallbackHandler } from "@passkeyme/react-auth";
import { BrowserRouter, Routes, Route } from "react-router-dom";

function App() {
  return (
    <BrowserRouter>
      <Routes>
        {/* Built-in callback handler - handles all authentication flows */}
        <Route path="/auth/callback" element={<PasskeymeCallbackHandler />} />
        <Route path="/" element={<Dashboard />} />
      </Routes>
    </BrowserRouter>
  );
}

function Dashboard() {
  const { user, loginWithOAuth, logout, loading } = usePasskeyme();

  if (loading) {
    return <div>Loading...</div>;
  }

  if (!user) {
    return (
      <button onClick={() => loginWithOAuth("google")}>
        Login with Google
      </button>
    );
  }

  return (
    <div>
      <h1>Welcome, {user.name}!</h1>
      <button onClick={logout}>Logout</button>
    </div>
  );
}

3. Use Pre-built Components

import {
  PasskeymeOAuthButton,
  PasskeymeButton,
  PasskeymeUserProfile,
  PasskeymeProtectedRoute,
} from "@passkeyme/react-auth";

function LoginPage() {
  return (
    <div>
      <PasskeymeOAuthButton provider="google" />
      <PasskeymeOAuthButton provider="github" />
      <PasskeymeButton />
    </div>
  );
}

function Dashboard() {
  return (
    <PasskeymeProtectedRoute fallback={<LoginPage />}>
      <div>
        <PasskeymeUserProfile showLogout />
        <h1>Protected Content</h1>
      </div>
    </PasskeymeProtectedRoute>
  );
}

📚 API Reference

PasskeymeProvider

The main provider component that manages authentication state.

<PasskeymeProvider
  config={{
    appId: "your-app-id",
    redirectUri: "http://localhost:3000/callback",
    debug: true, // Enable debug logging for development
  }}
  loadingComponent={<div>Loading...</div>}
  errorComponent={error => <div>Error: {error}</div>}
  onAuthChange={user => console.log("User changed:", user)}
  onError={error => console.error("Auth error:", error)}
>
  <App />
</PasskeymeProvider>

Note: Set debug: false or omit the debug flag in production to prevent debug messages from appearing in the browser console.

usePasskeyme Hook

Main hook providing full authentication functionality.

const {
  user, // Current user or null
  loading, // Loading state
  error, // Error message or null
  isAuthenticated, // Boolean auth status

  // Login methods
  login, // Generic login with options
  loginWithOAuth, // OAuth login (redirects)
  loginWithPasskey, // Passkey authentication
  loginWithPassword, // Username/password login
  handleCallback, // Handle OAuth callback

  // Other methods
  logout, // Logout user
  getAccessToken, // Get current token
  refreshToken, // Refresh token
  auth, // Direct access to auth instance
} = usePasskeyme();

useAuthState Hook

Get only the authentication state (no methods).

const { user, loading, error, isAuthenticated } = useAuthState();

useAuth Hook

Get only the authentication methods (no state).

const {
  login,
  loginWithOAuth,
  loginWithPasskey,
  loginWithPassword,
  logout,
  getAccessToken,
  refreshToken,
} = useAuth();

🧩 Components

PasskeymeCallbackHandler

⭐ NEW: Built-in component that automatically handles all authentication callbacks.

The PasskeymeCallbackHandler eliminates the need to write custom callback logic. Just add it to your routing:

import { PasskeymeCallbackHandler } from '@passkeyme/react-auth';

// Basic usage - handles everything automatically
<Route path="/callback" element={<PasskeymeCallbackHandler />} />

// With custom redirects and components
<Route
  path="/callback"
  element={
    <PasskeymeCallbackHandler
      successRedirect="/dashboard"
      errorRedirect="/login"
      loadingComponent={CustomSpinner}
      errorComponent={CustomError}
    />
  }
/>

Features:

  • ✅ Handles both OAuth (?code=...) and hosted auth (?token=...) flows
  • ✅ Built-in loading and error states with customizable components
  • ✅ Automatic token validation and user state management
  • NEW: Automatic passkey registration prompting (enabled by default)
  • ✅ Configurable success/error redirects
  • ✅ Custom event callbacks for advanced use cases
  • ✅ URL cleanup after processing

See CALLBACK_HANDLER.md for complete documentation.

PasskeymeOAuthButton

OAuth login button with built-in styling.

<PasskeymeOAuthButton
  provider="google" // 'google' | 'github' | 'facebook'
  variant="default" // 'default' | 'outlined' | 'text'
  size="medium" // 'small' | 'medium' | 'large'
  redirectUri="/custom/callback"
  disabled={false}
  loading={false}
  onClick={() => console.log("Clicked")}
  className="custom-class"
>
  Custom Button Text
</PasskeymeOAuthButton>

PasskeymeButton

Passkey authentication button.

<PasskeymeButton
  username="[email protected]" // Optional username hint
  variant="default"
  size="medium"
  onSuccess={user => console.log("Success:", user)}
  onError={error => console.log("Error:", error)}
>
  Sign in with Passkey
</PasskeymeButton>

PasskeymeUserProfile

User profile display with avatar and logout.

<PasskeymeUserProfile
  showAvatar={true}
  showName={true}
  showEmail={true}
  showLogout={true}
  avatarSize={40}
  logoutText="Sign Out"
  onLogout={() => console.log("Logged out")}
  className="profile-container"
/>

PasskeymeProtectedRoute

Protect content for authenticated users.

<PasskeymeProtectedRoute
  fallback={<LoginPage />}
  redirectTo="/login"
  requiredRoles={["admin", "user"]}
  hasAccess={user => user.emailVerified}
>
  <SecretContent />
</PasskeymeProtectedRoute>

withAuth HOC

Higher-order component for protecting components.

const ProtectedComponent = withAuth(MyComponent, {
  fallback: <div>Please login</div>,
  requiredRoles: ["admin"],
});

🚀 Programmatic Authentication

triggerPasskeymeAuth

⭐ NEW: Enhanced programmatic authentication with mode support for hosted vs inline OAuth flows.

// Simple hosted flow (default - redirects to hosted auth pages)
const { triggerPasskeymeAuth } = usePasskeyme();
triggerPasskeymeAuth();

// Custom success/error handling
triggerPasskeymeAuth({
  username: "[email protected]",
  onSuccess: (user, method) => console.log(`Authenticated via ${method}`, user),
  onError: error => console.error("Auth failed:", error),
});

// Inline mode - developer controls OAuth UI
triggerPasskeymeAuth({
  mode: "inline",
  onOAuthRequired: providers => {
    // Show your custom OAuth UI
    setShowOAuthModal(true);
    setAvailableProviders(providers);
  },
  onSuccess: (user, method) => {
    setShowOAuthModal(false);
    console.log(`Success via ${method}`, user);
  },
});

// Passkey-only (no fallback)
triggerPasskeymeAuth({
  forcePasskeyOnly: true,
  onError: error => alert("Passkey authentication failed"),
});

Mode Options:

  • hosted (default): Try passkey → redirect to hosted auth pages
  • inline: Try passkey → callback with OAuth providers for custom UI

This gives you maximum flexibility - use hosted mode for simple setup, or inline mode for full UI control.

Complete Inline Mode Example

function CustomAuthFlow() {
  const { triggerPasskeymeAuth } = usePasskeyme();
  const [showOAuth, setShowOAuth] = useState(false);
  const [providers, setProviders] = useState([]);

  const handleLogin = () => {
    triggerPasskeymeAuth({
      mode: "inline",
      onOAuthRequired: availableProviders => {
        setProviders(availableProviders);
        setShowOAuth(true);
      },
      onSuccess: (user, method) => {
        setShowOAuth(false);
        console.log(`Authenticated via ${method}`);
      },
    });
  };

  return (
    <div>
      <button onClick={handleLogin}>Login</button>

      {showOAuth && (
        <div className="oauth-modal">
          <h3>Choose OAuth Provider</h3>
          {providers.map(provider => (
            <PasskeymeOAuthButton key={provider} provider={provider}>
              Login with {provider}
            </PasskeymeOAuthButton>
          ))}
          <button onClick={() => setShowOAuth(false)}>Cancel</button>
        </div>
      )}
    </div>
  );
}

🎨 Customization

Custom Styling

All components accept className and can be styled with CSS:

.login-button {
  background: linear-gradient(45deg, #fe6b8b 30%, #ff8e53 90%);
  border-radius: 20px;
}

.user-profile {
  border: 2px solid #f0f0f0;
  border-radius: 8px;
  padding: 16px;
}

Custom Components

Create your own components using the hooks:

function CustomLoginForm() {
  const { loginWithPassword, loading, error } = usePasskeyme();
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");

  const handleSubmit = async e => {
    e.preventDefault();
    try {
      await loginWithPassword(email, password);
    } catch (err) {
      // Error handled by hook
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        value={email}
        onChange={e => setEmail(e.target.value)}
        placeholder="Email"
      />
      <input
        type="password"
        value={password}
        onChange={e => setPassword(e.target.value)}
        placeholder="Password"
      />
      <button type="submit" disabled={loading}>
        {loading ? "Signing in..." : "Sign In"}
      </button>
      {error && <div>Error: {error}</div>}
    </form>
  );
}

🔗 Integration Examples

Next.js App Router

// app/layout.tsx
import { PasskeymeProvider } from "@passkeyme/react-auth";

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <PasskeymeProvider
          config={{ appId: process.env.NEXT_PUBLIC_PASSKEYME_APP_ID }}
        >
          {children}
        </PasskeymeProvider>
      </body>
    </html>
  );
}

// app/dashboard/page.tsx
import { ProtectedRoute, UserProfile } from "@passkeyme/react-auth";

export default function Dashboard() {
  return (
    <ProtectedRoute>
      <div>
        <UserProfile />
        <h1>Dashboard</h1>
      </div>
    </ProtectedRoute>
  );
}

React Router

import { BrowserRouter, Route, Routes, Navigate } from "react-router-dom";
import { usePasskeyme } from "@passkeyme/react-auth";

function AppRouter() {
  const { isAuthenticated, loading } = usePasskeyme();

  if (loading) return <div>Loading...</div>;

  return (
    <BrowserRouter>
      <Routes>
        <Route path="/login" element={<LoginPage />} />
        <Route
          path="/dashboard"
          element={isAuthenticated ? <Dashboard /> : <Navigate to="/login" />}
        />
      </Routes>
    </BrowserRouter>
  );
}

🔗 TypeScript Support

This package provides comprehensive TypeScript definitions for type-safe authentication.

Type Imports

import type {
  User,
  AuthConfig,
  AuthState,
  PasskeyCredential,
  OAuthProvider,
  UsePasskeymeReturn,
} from "@passkeyme/react-auth";

// User object structure
interface User {
  id: string;
  email: string;
  name?: string;
  avatar?: string;
  metadata?: Record<string, any>;
}

// Authentication configuration
interface AuthConfig {
  appId: string;
  baseUrl?: string;
  redirectUri: string;
  debug?: boolean;
}

// Hook return type
interface UsePasskeymeReturn {
  // State
  user: User | null;
  isAuthenticated: boolean;
  loading: boolean;
  error: Error | null;

  // Actions
  loginWithOAuth: (provider: OAuthProvider) => Promise<void>;
  loginWithPasskey: () => Promise<void>;
  loginWithCredentials: (email: string, password: string) => Promise<void>;
  register: (email: string, password: string, name?: string) => Promise<void>;
  logout: () => Promise<void>;

  // Utility
  refreshToken: () => Promise<void>;
  getAuthHeader: () => string | null;
}

Type-Safe Configuration

import { PasskeymeProvider } from "@passkeyme/react-auth";
import type { AuthConfig } from "@passkeyme/react-auth";

const authConfig: AuthConfig = {
  appId: process.env.REACT_APP_PASSKEYME_APP_ID!,
  redirectUri: `${window.location.origin}/auth/callback`,
  debug: process.env.NODE_ENV === "development",
};

function App() {
  return (
    <PasskeymeProvider config={authConfig}>
      <AppContent />
    </PasskeymeProvider>
  );
}

Custom Hook with TypeScript

import { usePasskeyme } from "@passkeyme/react-auth";
import type { User } from "@passkeyme/react-auth";

// Custom hook for user profile management
function useUserProfile() {
  const { user, updateProfile } = usePasskeyme();

  const updateUserProfile = async (updates: Partial<User>) => {
    if (!user) throw new Error("User not authenticated");

    return updateProfile({
      ...user,
      ...updates,
    });
  };

  return {
    user,
    updateUserProfile,
    hasProfile: Boolean(user?.name),
    isEmailVerified: user?.emailVerified ?? false,
  };
}

🔄 Integration Guides

Next.js Integration

// pages/_app.tsx
import { AppProps } from "next/app";
import { PasskeymeProvider } from "@passkeyme/react-auth";

export default function App({ Component, pageProps }: AppProps) {
  return (
    <PasskeymeProvider
      config={{
        appId: process.env.NEXT_PUBLIC_PASSKEYME_APP_ID!,
        redirectUri: `${process.env.NEXT_PUBLIC_BASE_URL}/auth/callback`,
      }}
    >
      <Component {...pageProps} />
    </PasskeymeProvider>
  );
}

// pages/auth/callback.tsx
import { PasskeymeCallbackHandler } from "@passkeyme/react-auth";

export default function AuthCallback() {
  return <PasskeymeCallbackHandler />;
}

// Custom hook for Next.js router integration
import { useRouter } from "next/router";
import { usePasskeyme } from "@passkeyme/react-auth";
import { useEffect } from "react";

export function useAuthRedirect() {
  const { isAuthenticated, loading } = usePasskeyme();
  const router = useRouter();

  useEffect(() => {
    if (!loading && !isAuthenticated) {
      router.push("/login");
    }
  }, [isAuthenticated, loading, router]);

  return { isAuthenticated, loading };
}

Vite/React Integration

// main.tsx
import React from "react";
import ReactDOM from "react-dom/client";
import { PasskeymeProvider } from "@passkeyme/react-auth";
import App from "./App";

ReactDOM.createRoot(document.getElementById("root")!).render(
  <React.StrictMode>
    <PasskeymeProvider
      config={{
        appId: import.meta.env.VITE_PASSKEYME_APP_ID,
        redirectUri: `${window.location.origin}/auth/callback`,
        debug: import.meta.env.DEV,
      }}
    >
      <App />
    </PasskeymeProvider>
  </React.StrictMode>
);

// vite-env.d.ts - Environment variables
interface ImportMetaEnv {
  readonly VITE_PASSKEYME_APP_ID: string;
  // Add other env variables as needed
}

interface ImportMeta {
  readonly env: ImportMetaEnv;
}

React Router Integration

import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
import { PasskeymeCallbackHandler, usePasskeyme } from "@passkeyme/react-auth";

// Protected Route Component
interface ProtectedRouteProps {
  children: React.ReactNode;
  fallback?: React.ReactNode;
}

function ProtectedRoute({ children, fallback }: ProtectedRouteProps) {
  const { isAuthenticated, loading } = usePasskeyme();

  if (loading) return <div>Loading...</div>;

  if (!isAuthenticated) {
    return fallback ? <>{fallback}</> : <Navigate to="/login" replace />;
  }

  return <>{children}</>;
}

function AppRouter() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/login" element={<LoginPage />} />
        <Route path="/auth/callback" element={<PasskeymeCallbackHandler />} />
        <Route
          path="/dashboard"
          element={
            <ProtectedRoute>
              <Dashboard />
            </ProtectedRoute>
          }
        />
        <Route path="/" element={<Navigate to="/dashboard" replace />} />
      </Routes>
    </BrowserRouter>
  );
}

Error Handling Best Practices

import { usePasskeyme } from "@passkeyme/react-auth";
import { useEffect, useState } from "react";

function LoginWithErrorHandling() {
  const { loginWithOAuth, error, loading } = usePasskeyme();
  const [loginError, setLoginError] = useState<string | null>(null);

  const handleLogin = async (provider: "google" | "github") => {
    try {
      setLoginError(null);
      await loginWithOAuth(provider);
    } catch (err) {
      setLoginError(err instanceof Error ? err.message : "Login failed");
    }
  };

  // Handle authentication errors
  useEffect(() => {
    if (error) {
      console.error("Authentication error:", error);
      setLoginError(error.message);
    }
  }, [error]);

  return (
    <div>
      {loginError && (
        <div className="error-banner" role="alert">
          {loginError}
          <button onClick={() => setLoginError(null)}>×</button>
        </div>
      )}

      <button onClick={() => handleLogin("google")} disabled={loading}>
        {loading ? "Signing in..." : "Sign in with Google"}
      </button>
    </div>
  );
}

🧪 Testing Integration

Jest Testing Setup

// test-utils.tsx
import React from "react";
import { render, RenderOptions } from "@testing-library/react";
import { PasskeymeProvider } from "@passkeyme/react-auth";
import type { AuthConfig } from "@passkeyme/react-auth";

const mockAuthConfig: AuthConfig = {
  appId: "test-app-id",
  redirectUri: "http://localhost:3000/callback",
  debug: false,
};

interface CustomRenderOptions extends Omit<RenderOptions, "wrapper"> {
  authConfig?: Partial<AuthConfig>;
}

export function renderWithAuth(
  ui: React.ReactElement,
  { authConfig = {}, ...options }: CustomRenderOptions = {}
) {
  const config = { ...mockAuthConfig, ...authConfig };

  function Wrapper({ children }: { children: React.ReactNode }) {
    return <PasskeymeProvider config={config}>{children}</PasskeymeProvider>;
  }

  return render(ui, { wrapper: Wrapper, ...options });
}

// Component test example
import { screen, fireEvent, waitFor } from "@testing-library/react";
import { usePasskeyme } from "@passkeyme/react-auth";

function TestComponent() {
  const { user, loginWithOAuth, loading } = usePasskeyme();

  return (
    <div>
      {user ? (
        <div data-testid="user-info">{user.email}</div>
      ) : (
        <button onClick={() => loginWithOAuth("google")}>
          {loading ? "Loading..." : "Login"}
        </button>
      )}
    </div>
  );
}

test("handles login flow", async () => {
  renderWithAuth(<TestComponent />);

  const loginButton = screen.getByText("Login");
  fireEvent.click(loginButton);

  expect(screen.getByText("Loading...")).toBeInTheDocument();

  await waitFor(() => {
    expect(screen.getByTestId("user-info")).toBeInTheDocument();
  });
});

🔗 Package Integration

Using with @passkeyme/react-core

This package builds on top of @passkeyme/react-core for shared patterns:

// Access core patterns directly
import {
  usePasskeymeContext,
  PasskeymeProvider as CoreProvider,
} from "@passkeyme/react-core";

// Or use the enhanced React-specific provider
import { PasskeymeProvider } from "@passkeyme/react-auth";

// Custom hook using core patterns
function useCustomAuth() {
  const { state, dispatch } = usePasskeymeContext();

  const customLogin = async () => {
    dispatch({ type: "SET_LOADING", payload: true });
    try {
      // Custom authentication logic
      const user = await customAuthFlow();
      dispatch({ type: "SET_USER", payload: user });
    } catch (error) {
      dispatch({ type: "SET_ERROR", payload: error });
    } finally {
      dispatch({ type: "SET_LOADING", payload: false });
    }
  };

  return { ...state, customLogin };
}

Using with @passkeyme/auth Core

import { PasskeymeAuth } from "@passkeyme/auth";
import { usePasskeyme } from "@passkeyme/react-auth";

// Access the core auth instance
function AdvancedAuthComponent() {
  const { auth } = usePasskeyme();

  const handleAdvancedFlow = async () => {
    // Direct access to core SDK methods
    const tokens = await auth.getTokens();
    const userInfo = await auth.getUserInfo();

    // Custom API calls with auth headers
    const response = await fetch("/api/protected", {
      headers: {
        Authorization: `Bearer ${tokens.accessToken}`,
      },
    });
  };

  return <button onClick={handleAdvancedFlow}>Advanced Flow</button>;
}

🆚 vs Other Solutions

| Feature | @passkeyme/react-auth | Firebase Auth | Auth0 React | | --------------------- | ------------------------- | ----------------- | ------------------ | | Setup Lines | ✅ 5 lines | ❌ 20+ lines | ❌ 15+ lines | | Built-in Passkeys | ✅ Native support | ❌ Manual setup | ❌ Manual setup | | Type Safety | ✅ Full TypeScript | ⚠️ Partial | ✅ Full TypeScript | | Bundle Size | ✅ Small | ❌ Large | ❌ Large | | Self-Hosting | ✅ Yes | ❌ No | ❌ No | | Vendor Lock-in | ✅ None | ❌ High | ❌ High |

📄 License

MIT - see LICENSE file for details.

🤝 Support

  • Documentation: https://passkeyme.com/docs/react
  • Issues: https://github.com/passkeyme/passkeyme/issues
  • Discord: https://discord.gg/passkeyme