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

@personql/react

v1.0.7

Published

React SDK for PersonQL authentication and user management

Readme

PersonQL React SDK

A comprehensive React SDK for PersonQL authentication and user management. This SDK provides pre-built components, hooks, and utilities to quickly integrate PersonQL authentication into your React applications.

Features

  • 🚀 Out-of-the-box components - Ready-to-use Sign In, Sign Up, MFA, and Profile forms
  • 🎣 React Hooks - Easy-to-use hooks for authentication state management
  • 🔒 Security First - Built-in form validation, error handling, and secure token management
  • 🎨 Customizable UI - Themeable components with Tailwind CSS
  • 📱 Responsive Design - Mobile-friendly components
  • 🔄 MFA Support - Multi-factor authentication with SMS, Email, and WhatsApp
  • 🛡️ Route Protection - AuthGuard component for protecting routes
  • 📝 TypeScript Support - Full TypeScript definitions included

Installation

npm install @personql/react
# or
yarn add @personql/react

Quick Start

1. Setup the Provider

Wrap your app with the PersonQLProvider:

import React from 'react';
import { PersonQLProvider } from '@personql/react';

const config = {
  apiUrl: 'https://gateway.personql.com',
  clientId: 'your-client-id',
};

function App() {
  return (
    <PersonQLProvider config={config}>
      <YourAppContent />
    </PersonQLProvider>
  );
}

2. Use Authentication Components

import React, { useState } from 'react';
import { SignInForm, SignUpForm, useAuth } from '@personql/react';

function AuthPage() {
  const [showSignUp, setShowSignUp] = useState(false);
  const { isAuthenticated } = useAuth();

  if (isAuthenticated) {
    return <Dashboard />;
  }

  return (
    <div className="min-h-screen flex items-center justify-center">
      {showSignUp ? (
        <SignUpForm
          onSuccess={() => console.log('Sign up successful!')}
          onSignIn={() => setShowSignUp(false)}
        />
      ) : (
        <SignInForm
          onSuccess={() => console.log('Sign in successful!')}
          onSignUp={() => setShowSignUp(true)}
        />
      )}
    </div>
  );
}

3. Protect Routes with AuthGuard

import React from 'react';
import { AuthGuard } from '@personql/react';

function ProtectedPage() {
  return (
    <AuthGuard
      fallback={<div>Please sign in to access this page</div>}
      requireVerification={true}
    >
      <h1>Protected Content</h1>
      <p>Only authenticated and verified users can see this.</p>
    </AuthGuard>
  );
}

Components

Authentication Forms

SignInForm

import { SignInForm } from '@personql/react';

<SignInForm
  onSuccess={() => navigate('/dashboard')}
  onSignUp={() => setShowSignUp(true)}
  onForgotPassword={() => setShowForgotPassword(true)}
  className="max-w-md mx-auto"
/>;

SignUpForm

import { SignUpForm } from '@personql/react';

<SignUpForm
  onSuccess={() => navigate('/dashboard')}
  onSignIn={() => setShowSignUp(false)}
  className="max-w-md mx-auto"
/>;

MFAForm

import { MFAForm } from '@personql/react';

<MFAForm
  onSuccess={() => navigate('/dashboard')}
  onCancel={() => navigate('/signin')}
/>;

ForgotPasswordForm

import { ForgotPasswordForm } from '@personql/react';

<ForgotPasswordForm
  onSuccess={() => setEmailSent(true)}
  onBackToSignIn={() => navigate('/signin')}
/>;

ResetPasswordForm

import { ResetPasswordForm } from '@personql/react';

<ResetPasswordForm token={resetToken} onSuccess={() => navigate('/signin')} />;

ProfileForm

import { ProfileForm } from '@personql/react';

<ProfileForm
  onSuccess={() => setShowSuccess(true)}
  className="max-w-2xl mx-auto"
/>;

Route Protection

AuthGuard

import { AuthGuard } from '@personql/react';

<AuthGuard
  fallback={<SignInPrompt />}
  redirectTo="/signin"
  requireVerification={true}
>
  <ProtectedContent />
</AuthGuard>;

Hooks

useAuth

Main authentication hook:

import { useAuth } from '@personql/react';

function MyComponent() {
  const {
    // State
    isAuthenticated,
    isLoading,
    error,
    user,
    requiresMFA,
    mfaMethods,

    // Methods
    signIn,
    signUp,
    signOut,
    sendMFACode,
    verifyMFACode,
    forgotPassword,
    resetPassword,
    clearError,
    isSessionExpired,
  } = useAuth();

  const handleSignIn = async () => {
    try {
      await signIn({
        email: '[email protected]',
        password: 'password123',
        rememberMe: true,
      });
    } catch (error) {
      console.error('Sign in failed:', error);
    }
  };

  return (
    <div>
      {isAuthenticated ? (
        <p>Welcome, {user?.firstName}!</p>
      ) : (
        <button onClick={handleSignIn}>Sign In</button>
      )}
    </div>
  );
}

useUser

User-specific hook:

import { useUser } from '@personql/react';

function UserProfile() {
  const {
    // State
    user,
    isLoading,
    error,

    // Methods
    updateProfile,
    refreshUser,

    // Computed values
    isVerified,
    fullName,
    initials,
  } = useUser();

  const handleUpdateProfile = async () => {
    await updateProfile({
      firstName: 'John',
      lastName: 'Doe',
    });
  };

  return (
    <div>
      <h1>Hello, {fullName}!</h1>
      <p>Verification status: {isVerified ? 'Verified' : 'Pending'}</p>
      <div className="avatar">{initials}</div>
    </div>
  );
}

usePersonQL

Access to the full PersonQL context:

import { usePersonQL } from '@personql/react';

function MyComponent() {
  const {
    authState,
    config,
    signIn,
    signUp,
    // ... all auth methods
  } = usePersonQL();

  return <div>...</div>;
}

Configuration

PersonQLConfig

interface PersonQLConfig {
  apiUrl: string; // Your Gateway Worker URL
  clientId: string; // Your application client ID
  redirectUri?: string; // Redirect URI after auth
  scopes?: string[]; // Permission scopes
  theme?: Theme; // UI customization
}

interface Theme {
  primaryColor?: string;
  backgroundColor?: string;
  textColor?: string;
  borderColor?: string;
  borderRadius?: string;
  fontFamily?: string;
}

Example with Custom Theme

const config = {
  apiUrl: 'https://gateway.personql.com',
  clientId: 'your-client-id',
  theme: {
    primaryColor: '#3b82f6',
    backgroundColor: '#ffffff',
    textColor: '#1f2937',
    borderColor: '#d1d5db',
    borderRadius: '0.375rem',
    fontFamily: 'Inter, sans-serif',
  },
};

<PersonQLProvider config={config}>
  <App />
</PersonQLProvider>;

Error Handling

The SDK provides comprehensive error handling:

import { useAuth } from '@personql/react';

function MyComponent() {
  const { error, clearError } = useAuth();

  if (error) {
    return (
      <Alert
        variant="error"
        title={error.code}
        message={error.message}
        dismissible
        onDismiss={clearError}
      />
    );
  }

  return <div>No errors</div>;
}

Validation

Built-in form validation utilities:

import { validateEmail, validatePassword } from '@personql/react';

const emailValidation = validateEmail('[email protected]');
const passwordValidation = validatePassword('password123', {
  minLength: 8,
  requireUppercase: true,
  requireLowercase: true,
  requireNumbers: true,
  requireSpecialChars: true,
});

if (!emailValidation.isValid) {
  console.error(emailValidation.message);
}

TypeScript Support

The SDK is fully typed with TypeScript:

import type {
  PersonQLConfig,
  AuthState,
  User,
  SignInCredentials,
  SignUpCredentials,
  MFACredentials,
  AuthError,
} from '@personql/react';

Examples

Complete Authentication Flow

import React, { useState } from 'react';
import {
  PersonQLProvider,
  SignInForm,
  SignUpForm,
  MFAForm,
  ForgotPasswordForm,
  ResetPasswordForm,
  useAuth,
} from '@personql/react';

const config = {
  apiUrl: 'https://gateway.personql.com',
  clientId: 'your-client-id',
};

function AuthFlow() {
  const [view, setView] = useState<
    'signin' | 'signup' | 'mfa' | 'forgot' | 'reset'
  >('signin');
  const { isAuthenticated, requiresMFA } = useAuth();

  if (isAuthenticated) {
    return <Dashboard />;
  }

  if (requiresMFA) {
    return (
      <MFAForm
        onSuccess={() => setView('signin')}
        onCancel={() => setView('signin')}
      />
    );
  }

  return (
    <div className="min-h-screen flex items-center justify-center">
      {view === 'signin' && (
        <SignInForm
          onSuccess={() => console.log('Signed in!')}
          onSignUp={() => setView('signup')}
          onForgotPassword={() => setView('forgot')}
        />
      )}
      {view === 'signup' && (
        <SignUpForm
          onSuccess={() => console.log('Signed up!')}
          onSignIn={() => setView('signin')}
        />
      )}
      {view === 'forgot' && (
        <ForgotPasswordForm
          onSuccess={() => setView('signin')}
          onBackToSignIn={() => setView('signin')}
        />
      )}
      {view === 'reset' && (
        <ResetPasswordForm
          token="reset-token"
          onSuccess={() => setView('signin')}
        />
      )}
    </div>
  );
}

function App() {
  return (
    <PersonQLProvider config={config}>
      <AuthFlow />
    </PersonQLProvider>
  );
}

Multi-Tenant Support

PersonQL React SDK includes built-in support for multi-tenant applications with organizations, roles, and permissions.

useOrganization Hook

Manage organizations in your multi-tenant application:

import { useOrganization } from '@personql/react';

function MyComponent() {
  const {
    // State
    currentOrganization,
    organizations,
    loading,
    switching,
    currentRole,
    members,
    roles,

    // Methods
    switchOrganization,
    createOrganization,
    updateOrganization,
    hasPermission,
    loadMembers,
    loadRoles,
    refreshOrganizations,
  } = useOrganization();

  return (
    <div>
      <h2>{currentOrganization?.name}</h2>
      {hasPermission('users:write') && (
        <button>Add Team Member</button>
      )}
    </div>
  );
}

OrganizationSwitcher Component

Pre-built UI component for switching organizations:

import { OrganizationSwitcher } from '@personql/react';

function Header() {
  return (
    <header>
      <OrganizationSwitcher
        onOrganizationChange={(orgId) => {
          console.log('Switched to:', orgId);
        }}
        showCreateButton={true}
        onCreateClick={() => setShowCreateModal(true)}
      />
    </header>
  );
}

Creating Organizations

import { useOrganization } from '@personql/react';

function CreateOrganizationButton() {
  const { createOrganization } = useOrganization();

  const handleCreate = async () => {
    try {
      const newOrg = await createOrganization({
        name: 'My New Organization',
        slug: 'my-new-org',
      });
      console.log('Created:', newOrg);
    } catch (error) {
      console.error('Failed to create organization:', error);
    }
  };

  return <button onClick={handleCreate}>Create Organization</button>;
}

Switching Organizations

import { useOrganization } from '@personql/react';

function OrganizationList() {
  const { organizations, currentOrganization, switchOrganization } = useOrganization();

  return (
    <ul>
      {organizations.map((org) => (
        <li key={org.id}>
          <button
            onClick={() => switchOrganization(org.id)}
            disabled={org.id === currentOrganization?.id}
          >
            {org.name}
            {org.id === currentOrganization?.id && ' (Current)'}
          </button>
        </li>
      ))}
    </ul>
  );
}

Permission-Based Rendering

import { useOrganization } from '@personql/react';

function AdminPanel() {
  const { hasPermission } = useOrganization();

  if (!hasPermission('settings:write')) {
    return <div>Access Denied</div>;
  }

  return (
    <div>
      <h1>Admin Settings</h1>
      {/* Admin content */}
    </div>
  );
}

Loading Organization Members

import { useOrganization } from '@personql/react';
import { useEffect } from 'react';

function TeamMembers() {
  const { members, loadMembers, currentOrganization } = useOrganization();

  useEffect(() => {
    if (currentOrganization) {
      loadMembers();
    }
  }, [currentOrganization?.id]);

  return (
    <div>
      <h2>Team Members</h2>
      <ul>
        {members.map((member) => (
          <li key={member.id}>User ID: {member.user_id}</li>
        ))}
      </ul>
    </div>
  );
}

Organization Types

interface Organization {
  id: string;
  name: string;
  slug: string;
  domain?: string;
  logo_url?: string;
  primary_color?: string;
  plan_type: 'free' | 'starter' | 'pro' | 'enterprise';
  max_users: number;
  max_api_calls: number;
  settings?: Record<string, any>;
  status: 'active' | 'suspended' | 'deleted';
  created_at: string;
  updated_at: string;
}

interface Role {
  id: string;
  organization_id: string;
  name: string;
  description?: string;
  permissions: string[];
  is_system: boolean;
  created_at: string;
  updated_at: string;
}

interface OrganizationMember {
  id: string;
  organization_id: string;
  user_id: string;
  role_id: string;
  status: 'pending' | 'active' | 'suspended';
  created_at: string;
  updated_at: string;
}

Permission Format

Permissions follow the format resource:action where action can be:

  • read - View the resource
  • write - Create/update the resource
  • delete - Delete the resource
  • * - All actions (wildcard)

Special permissions:

  • * - Full access to everything
  • resource:* - All actions on a specific resource (e.g., users:*)

Example permissions:

  • users:read - Can view users
  • users:write - Can create/update users
  • users:* - All user operations
  • settings:write - Can update settings
  • billing:read - Can view billing information

License

MIT License - see LICENSE file for details.

Support

For support and questions:

Usage

import { PersonQLProvider } from '@personql/react';

<PersonQLProvider apiKey="your-api-key">{/* your app */}</PersonQLProvider>;

// All requests will use the gateway-worker URL automatically.