A modern Next.js boilerplate with NextAuth.js authentication, TypeScript, and comprehensive API layer.
- Next.js 15 with App Router
- NextAuth.js with multiple authentication providers
- Tailwind CSS for styling
- TypeScript for type safety
- Protected Routes with middleware
- Custom Hooks for authentication and API calls
- Responsive Design
- Comprehensive API Layer with axios
- Request/Response Interceptors
- Error Handling & Retry Logic
- File Upload/Download Support
- Rate Limiting & Caching
- Performance Optimizations
- Image Optimization Components
- Lazy Loading Utilities
- Code Splitting Helpers
- Bundle Analyzer Setup
- Performance Monitoring Tools
- OptimizedImage Component - Next.js Image with lazy loading, blur placeholders
- Responsive sizing - Automatic image sizing based on device
- WebP/AVIF support - Modern image formats for better compression
- Blur placeholder - Smooth loading experience
- Component lazy loading - Dynamic imports with Suspense
- Image lazy loading - Intersection Observer based loading
- Data lazy loading - Progressive data loading
- Virtual scrolling - For large lists
- Route-based splitting - Automatic code splitting by routes
- Component-based splitting - Dynamic component loading
- Bundle splitting utilities - Manual chunk management
- Performance monitoring - Track chunk loading times
- Core Web Vitals - FCP, LCP, FID, CLS tracking
- Custom metrics - Function performance monitoring
- API call monitoring - Request/response timing
- Performance scoring - 0-100 performance score
- Axios-based HTTP client with interceptors
- Automatic token management (JWT refresh)
- Request/Response interceptors for common operations
- Error handling with standardized error format
- File upload/download support
- Request timeout and retry logic
- AuthService - Authentication operations
- UserService - User management CRUD operations
- CommonService - General utilities (health check, file upload, etc.)
- useApi - For API calls with loading/error states
- useApiAction - For API actions without return data
- usePaginatedApi - For paginated data loading
- Retry logic with exponential backoff
- Rate limiting utilities
- Request caching system
- URL parameter builders
- Error formatters
This boilerplate includes the following authentication providers:
- Google OAuth - Sign in with Google account
- GitHub OAuth - Sign in with GitHub account
- Credentials - Email/password authentication (demo mode)
yarn install
Copy the example environment file and configure your variables:
cp env.example .env.local
Required environment variables:
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=your-secret-key-here
# Google OAuth (optional)
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
# GitHub OAuth (optional)
GITHUB_ID=your-github-client-id
GITHUB_SECRET=your-github-client-secret
# API Configuration
NEXT_PUBLIC_API_URL=http://localhost:3000/api
NEXT_PUBLIC_API_TIMEOUT=10000
# File Upload Configuration
NEXT_PUBLIC_MAX_FILE_SIZE=5242880
NEXT_PUBLIC_ALLOWED_FILE_TYPES=image/jpeg,image/png,image/gif,application/pdf
You can generate a secure secret using:
openssl rand -base64 32
yarn dev
Open http://localhost:3000 to view the application.
import { OptimizedImage } from '@/components/ui/OptimizedImage'
function ProductCard({ product }) {
return (
<div className="product-card">
<OptimizedImage
src={product.image}
alt={product.name}
width={300}
height={200}
priority={false}
quality={75}
placeholder="blur"
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
/>
<h3>{product.name}</h3>
</div>
)
}
import { lazyLoad, useLazyData } from '@/utils/lazy-loading'
// Lazy load component
const HeavyComponent = lazyLoad(() => import('./HeavyComponent'), <div>Loading...</div>)
// Lazy load data
function UserList() {
const { data: users, loading, error } = useLazyData(() => fetch('/api/users').then((res) => res.json()))
if (loading) return <div>Loading users...</div>
if (error) return <div>Error: {error.message}</div>
return (
<div>
{users?.map((user) => (
<div key={user.id}>{user.name}</div>
))}
</div>
)
}
import { createRouteLoader, bundleSplitter } from '@/utils/code-splitting'
// Route-based splitting
const DashboardLoader = createRouteLoader('dashboard')
// Component-based splitting
const ChartLoader = createComponentLoader('charts/LineChart')
// Manual chunk management
bundleSplitter.registerChunk('analytics', () => import('./analytics'))
await bundleSplitter.loadChunk('analytics')
import { performanceMonitor, measureTime } from '@/utils/performance-monitor'
// Monitor function performance
const expensiveFunction = measureTime(() => {
// Expensive operation
}, 'expensive-operation')
// Monitor API calls
const apiMonitor = performanceMonitor.monitorApiCall('/api/users', 'GET')
const response = await fetch('/api/users')
apiMonitor.end()
// Get performance metrics
const metrics = performanceMonitor.getMetrics()
const score = performanceMonitor.getPerformanceScore()
console.log('Performance Score:', score)
console.log('LCP:', metrics.lcp)
To analyze your bundle size:
yarn build:analyze
This will generate a bundle analysis report at ./bundle-analysis.html
.
import { api } from '@/utils/api'
// GET request
const response = await api.get<User>('/users/1')
// POST request
const newUser = await api.post<User>('/users', {
name: 'John Doe',
email: 'john@example.com'
})
// File upload
const fileResponse = await api.upload('/upload', file)
import { AuthService, UserService } from '@/services'
// Authentication
const loginResponse = await AuthService.login({
email: 'user@example.com',
password: 'password'
})
// User management
const users = await UserService.getUsers({
page: 1,
limit: 10,
search: 'john'
})
import { useApi, useApiAction } from '@/hooks/useApi'
import { UserService } from '@/services'
function UserList() {
const { data: users, loading, error, execute } = useApi(UserService.getUsers)
const { loading: deleteLoading, execute: deleteUser } = useApiAction(UserService.deleteUser)
useEffect(() => {
execute()
}, [execute])
if (loading) return <div>Loading...</div>
if (error) return <div>Error: {error.message}</div>
return (
<div>
{users?.data?.map((user) => (
<div key={user.id}>
{user.name}
<button onClick={() => deleteUser(user.id)}>Delete</button>
</div>
))}
</div>
)
}
import { usePaginatedApi } from '@/hooks/useApi'
import { UserService } from '@/services'
function PaginatedUserList() {
const { data, loading, hasMore, loadData, reset } = usePaginatedApi(UserService.getUsers)
useEffect(() => {
loadData({ limit: 20 }, true) // Reset and load first page
}, [])
const loadMore = () => {
if (hasMore && !loading) {
loadData({ limit: 20 }) // Load next page
}
}
return (
<div>
{data.map((user) => (
<div key={user.id}>{user.name}</div>
))}
{hasMore && (
<button onClick={loadMore} disabled={loading}>
{loading ? 'Loading...' : 'Load More'}
</button>
)}
</div>
)
}
For testing purposes, you can use these demo credentials:
- Email: admin@example.com
- Password: password
- Go to Google Cloud Console
- Create a new project or select existing one
- Enable Google+ API
- Go to Credentials → Create Credentials → OAuth 2.0 Client ID
- Add authorized redirect URI:
http://localhost:3000/api/auth/callback/google
- Copy Client ID and Client Secret to your
.env.local
- Go to GitHub Developer Settings
- Click "New OAuth App"
- Set Authorization callback URL:
http://localhost:3000/api/auth/callback/github
- Copy Client ID and Client Secret to your
.env.local
src/
├── app/
│ ├── api/auth/[...nextauth]/route.ts # NextAuth API route
│ ├── dashboard/page.tsx # Protected dashboard page
│ ├── login/page.tsx # Login page
│ ├── layout.tsx # Root layout
│ └── page.tsx # Home page
├── components/
│ ├── ui/
│ │ └── OptimizedImage.tsx # Optimized image component
│ ├── auth/
│ │ ├── ProtectedRoute.tsx # Route protection component
│ │ └── UserMenu.tsx # User menu component
│ ├── layout/
│ │ └── Header.tsx # Header component
│ └── providers/
│ └── SessionProvider.tsx # NextAuth session provider
├── hooks/
│ ├── useAuth.ts # Custom auth hook
│ └── useApi.ts # API hooks
├── services/
│ ├── auth.service.ts # Authentication service
│ ├── user.service.ts # User management service
│ ├── common.service.ts # Common utilities service
│ └── index.ts # Service exports
├── types/
│ ├── api.ts # API type definitions
│ └── next-auth.d.ts # NextAuth type extensions
├── utils/
│ ├── api.ts # Main API client
│ ├── api-helpers.ts # API utilities
│ ├── lazy-loading.ts # Lazy loading utilities
│ ├── code-splitting.ts # Code splitting utilities
│ ├── performance-monitor.ts # Performance monitoring
│ └── index.ts # Utility exports
└── middleware.ts # Authentication middleware
The API layer includes comprehensive error handling:
try {
const response = await api.get('/users')
// Handle success
} catch (error) {
// error is of type ApiError
console.log(error.message) // Error message
console.log(error.status) // HTTP status code
console.log(error.errors) // Field-specific errors
}
The API layer supports file uploads:
// Single file upload
const fileInput = document.querySelector('input[type="file"]')
const file = fileInput.files[0]
const response = await api.upload('/upload', file)
// Multiple file upload
const files = Array.from(fileInput.files)
const response = await api.post('/upload/multiple', formData, {
headers: { 'Content-Type': 'multipart/form-data' }
})
yarn dev # Start development server
yarn build # Build for production
yarn build:analyze # Build with bundle analysis
yarn start # Start production server
yarn lint # Run ESLint
yarn lint:fix # Fix ESLint errors
yarn fmt # Format code with Prettier
yarn fmt:check # Check code formatting
- Create a new service file in
src/services/
- Import the API client:
import { api } from '@/utils/api'
- Define your service class with static methods
- Export from
src/services/index.ts
You can extend the API client by modifying src/utils/api.ts
:
// Add custom request interceptor
apiClient.interceptors.request.use((config) => {
// Your custom logic
return config
})
// Add custom response interceptor
apiClient.interceptors.response.use((response) => {
// Your custom logic
return response
})
The API client automatically uses environment variables:
NEXT_PUBLIC_API_URL
- Base API URLNEXT_PUBLIC_API_TIMEOUT
- Request timeout
This project is licensed under the MIT License - see the LICENSE file for details.