A modular and reusable authentication feature built with React, TypeScript, Zod, React Query, and Zustand.
- ✅ User login and signup
- ✅ Password reset functionality
- ✅ Protected routes
- ✅ Form validation with Zod
- ✅ State management with Zustand
- ✅ API integration with React Query
- ✅ TypeScript support
- ✅ Tailwind CSS styling
- ✅ Fully reusable and modular
This module requires the following dependencies:
npm install @tanstack/react-query zustand zod react-hook-form @hookform/resolversOr with yarn:
yarn add @tanstack/react-query zustand zod react-hook-form @hookform/resolversWrap your app with the AuthProvider component:
import { AuthProvider } from './auth';
function App() {
return (
<AuthProvider>
<YourApp />
</AuthProvider>
);
}import { useState } from 'react';
import { LoginForm, SignupForm } from './auth';
import { useAuth } from './auth';
function AuthPage() {
const [mode, setMode] = useState<'login' | 'signup'>('login');
const { isAuthenticated } = useAuth();
if (isAuthenticated) {
return <div>Already logged in!</div>;
}
return (
<div className="max-w-md mx-auto mt-8 p-6 bg-white rounded-lg shadow-md">
{mode === 'login' ? (
<LoginForm
onSuccess={() => console.log('Logged in!')}
onSwitchToSignup={() => setMode('signup')}
/>
) : (
<SignupForm
onSuccess={() => console.log('Signed up!')}
onSwitchToLogin={() => setMode('login')}
/>
)}
</div>
);
}import { ProtectedRoute } from './auth';
function Dashboard() {
return (
<ProtectedRoute>
<div>Protected Dashboard Content</div>
</ProtectedRoute>
);
}import { useAuth, useLogout } from './auth';
function UserProfile() {
const { user, isAuthenticated } = useAuth();
const logoutMutation = useLogout();
if (!isAuthenticated) return null;
return (
<div>
<p>Welcome, {user?.email}!</p>
<button onClick={() => logoutMutation.mutate()}>
Logout
</button>
</div>
);
}Update the API base URL in auth/services/authService.ts:
const API_BASE_URL = import.meta.env.VITE_API_URL || '/api';Or set it via environment variable:
VITE_API_URL=https://your-api.comYour backend should implement these endpoints:
POST /auth/login- Login with email and passwordPOST /auth/signup- Create new user accountPOST /auth/logout- Logout userPOST /auth/forgot-password- Request password resetPOST /auth/reset-password- Confirm password reset with tokenPOST /auth/change-password- Change password (requires auth)POST /auth/refresh- Refresh authentication tokenGET /auth/me- Get current user profile
All auth endpoints should return:
{
user: {
id: string;
email: string;
name?: string;
// ... other user fields
},
token: string;
refreshToken?: string;
}Edit auth/schemas/index.ts:
const passwordRequirements = {
minLength: 8,
requireUppercase: true,
requireLowercase: true,
requireNumber: true,
requireSpecialChar: false, // Change to true to require special characters
};Update auth/types/index.ts to match your user model:
export interface User {
id: string;
email: string;
name?: string;
role?: string;
// Add your custom fields
}All components use Tailwind CSS classes. You can:
- Override with custom classes via the
classNameprop - Modify the components directly
- Use Tailwind's configuration to customize the design system
LoginForm- Login form with email and passwordSignupForm- Signup form with validationForgotPasswordForm- Request password resetResetPasswordForm- Confirm password reset with tokenChangePasswordForm- Change password for authenticated usersProtectedRoute- Route wrapper that requires authenticationAuthProvider- Context provider for auth state
useAuth()- Get current auth stateuseLogin()- Login mutation hookuseSignup()- Signup mutation hookuseLogout()- Logout mutation hookuseResetPassword()- Request password resetuseConfirmPasswordReset()- Confirm password resetuseChangePassword()- Change passworduseCurrentUser()- Fetch current useruseRefreshAuth()- Refresh auth token
The auth state is managed with Zustand and persisted to localStorage. Access the store directly if needed:
import { useAuthStore } from './auth';
const { user, token, isAuthenticated } = useAuthStore();import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import {
LoginForm,
SignupForm,
ForgotPasswordForm,
useAuth,
} from './auth';
function AuthPage() {
const [view, setView] = useState<'login' | 'signup' | 'forgot'>('login');
const { isAuthenticated } = useAuth();
const navigate = useNavigate();
if (isAuthenticated) {
navigate('/dashboard');
return null;
}
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50">
<div className="max-w-md w-full bg-white rounded-lg shadow-md p-8">
{view === 'login' && (
<LoginForm
onSuccess={() => navigate('/dashboard')}
onSwitchToSignup={() => setView('signup')}
onForgotPassword={() => setView('forgot')}
/>
)}
{view === 'signup' && (
<SignupForm
onSuccess={() => navigate('/dashboard')}
onSwitchToLogin={() => setView('login')}
/>
)}
{view === 'forgot' && (
<ForgotPasswordForm
onSuccess={() => setView('login')}
onBackToLogin={() => setView('login')}
/>
)}
</div>
</div>
);
}import { ProtectedRoute, useAuth, useLogout } from './auth';
function Dashboard() {
const { user } = useAuth();
const logout = useLogout();
return (
<ProtectedRoute>
<div className="p-8">
<h1>Dashboard</h1>
<p>Welcome, {user?.email}!</p>
<button
onClick={() => logout.mutate()}
className="mt-4 px-4 py-2 bg-red-600 text-white rounded"
>
Logout
</button>
</div>
</ProtectedRoute>
);
}To use this auth module in a different project:
- Copy the entire
authfolder to your new project - Install required dependencies
- Update API endpoints in
auth/services/authService.ts - Customize types in
auth/types/index.tsto match your backend - Wrap your app with
AuthProvider - Start using the components and hooks!
This module is provided as-is for reuse in your projects.