A production-ready Next.js 15 starter template with authentication, form handling, and a scalable architecture.
- β‘ Next.js 15.5 with App Router and Turbopack
- π Authentication - Cookie-based auth with protected routes
- π Form Handling - TanStack Form + Zod validation
- π¨ UI Components - shadcn/ui + Tailwind CSS 4
- π State Management - TanStack Query v5 + Zustand for client state
- π― TypeScript - Full type safety with enums
- πͺ Custom Hooks - Reusable logic patterns
- π± Responsive - Mobile-first design
- π Toast Notifications - Sonner for user feedback
- π¨ Theme Support - Next Themes for dark/light mode
- Framework: Next.js 15.5.6
- Language: TypeScript 5
- Styling: Tailwind CSS 4 + tw-animate-css
- UI Library: Radix UI + shadcn/ui
- Forms: TanStack Form 1.23 + Zod 4.1
- Data Fetching: TanStack Query 5.90 + Axios 1.13
- State Management: Zustand 5.0
- Icons: Lucide React
- Notifications: Sonner 2.0
- Node.js 20+ and pnpm (or npm/yarn/bun)
- Docker (optional, for containerized deployment)
- Clone the repository
git clone <your-repo-url>
cd nextjs-template- Install dependencies
pnpm install- Set up environment variables
cp .env.example .envEdit .env with your configuration.
- Run the development server
pnpm devThe app will hot-reload as you edit files.
nextjs-template/
βββ src/
β βββ app/ # Next.js App Router (routes & API)
β β βββ page.tsx # Home page (/)
β β βββ layout.tsx # Root layout
β β βββ globals.css # Global styles
β β βββ favicon.ico # App favicon
β β βββ auth/ # Auth routes (/auth/*)
β β β βββ page.tsx # Auth wrapper page
β β β βββ login/ # Login page
β β β βββ register/ # Register page
β β βββ user/ # Protected routes (/user/*)
β β β βββ page.tsx # User dashboard
β β βββ api/ # API endpoints
β β βββ auth/ # Auth API routes
β β βββ login/ # POST /api/auth/login
β β βββ register/ # POST /api/auth/register
β β βββ logout/ # POST /api/auth/logout
β β βββ me/ # GET /api/auth/me
β βββ modules/ # Feature modules
β β βββ auth/ # Auth feature
β β β βββ auth-page.tsx # Auth layout component
β β β βββ _components/ # Private auth components
β β β βββ login-form.tsx
β β β βββ register-form.tsx
β β βββ home/ # Home page feature
β β β βββ home-page.tsx
β β βββ user/ # User dashboard feature
β β βββ dashboard-page.tsx
β βββ components/ # Reusable UI components
β β βββ ui/ # shadcn/ui base components
β β β βββ button.tsx
β β β βββ card.tsx
β β β βββ field.tsx
β β β βββ input.tsx
β β β βββ input-group.tsx
β β β βββ label.tsx
β β β βββ separator.tsx
β β β βββ sonner.tsx
β β β βββ tabs.tsx
β β β βββ textarea.tsx
β β βββ custom/ # Custom components
β β βββ link.tsx
β βββ hooks/ # Custom React hooks
β β βββ index.ts # Hooks barrel export
β β βββ use-toggle.ts # Toggle hook
β βββ services/ # API service layer (TanStack Query)
β β βββ auth-queries.ts # Read operations (GET)
β β βββ auth-mutations.ts # Write operations (POST/PUT/DELETE)
β βββ schemas/ # Zod validation schemas
β β βββ auth.schema.ts # Auth form schemas
β βββ types/ # TypeScript type definitions
β β βββ auth.type.ts # Auth types
β βββ enums/ # TypeScript enums
β β βββ user-role.enum.ts # User role enum
β βββ store/ # Zustand stores for client state
β β βββ index.ts # Store barrel export
β β βββ counter.store.ts # Example counter store
β βββ lib/ # Utilities and configured libraries
β β βββ axios.ts # Axios instance config
β β βββ utils.ts # Utility functions (cn, etc.)
β βββ providers/ # React context providers
β β βββ react-query-provider.tsx
β βββ middleware.ts # Next.js middleware (auth protection)
βββ public/ # Static assets
βββ ...config files # See below for details
| File | Purpose |
|---|---|
next.config.ts |
Next.js configuration |
tsconfig.json |
TypeScript configuration |
tailwind.config.ts |
Tailwind CSS configuration |
postcss.config.mjs |
PostCSS configuration |
eslint.config.mjs |
ESLint configuration |
.prettierrc |
Prettier code formatting |
components.json |
shadcn/ui configuration |
dockerfile |
Docker image definition |
docker-compose.yml |
Docker Compose setup |
.env.example |
Environment variables template |
| Folder | Purpose | Example |
|---|---|---|
/app |
Next.js routes and API endpoints | app/auth/login/page.tsx |
/modules |
Feature-specific code | modules/auth/_components/login-form.tsx |
/components |
Reusable UI components | components/ui/button.tsx |
/hooks |
Custom React hooks | hooks/use-toggle.ts |
/services |
API calls with TanStack Query | services/auth-mutations.ts |
/schemas |
Zod validation schemas | schemas/auth.schema.ts |
/types |
TypeScript types | types/auth.type.ts |
/store |
Zustand stores for client state | store/counter.store.ts |
/providers |
React context providers | providers/react-query-provider.tsx |
- Files/Folders:
kebab-case(e.g.,login-form.tsx) - Components:
PascalCase(e.g.,LoginForm) - Functions/Variables:
camelCase(e.g.,useAuthLogin) - Private folders:
_components/(module-specific, not reusable) - Imports: Use
@/alias (e.g.,import { Button } from '@/components/ui/button')
pnpm dev # Start development server with Turbopack
pnpm build # Build for production with Turbopack
pnpm start # Start production server
pnpm lint # Run ESLintNote: This template uses Turbopack for both development and production builds for faster performance.
This template includes a complete authentication system:
- Login/Register - Forms with validation at
/auth/loginand/auth/register - Cookie-based Auth - HTTP-only cookies for security
- Protected Routes - Middleware protects
/user/*routes - Auto-redirect - Logged-in users can't access auth pages
- User Query -
useAuthUser()hook fetches current user
POST /api/auth/login- User loginPOST /api/auth/register- User registrationPOST /api/auth/logout- User logoutGET /api/auth/me- Get current user
- Create module structure:
mkdir -p src/modules/blog/_components- Create page component:
// src/modules/blog/blog-page.tsx
export default function BlogPage() {
return <div>Blog</div>;
}- Create route:
// src/app/blog/page.tsx
import BlogPage from '@/modules/blog/blog-page';
export default BlogPage;- Add services, schemas, and types as needed:
src/services/blog-queries.ts
src/services/blog-mutations.ts
src/schemas/blog.schema.ts
src/types/blog.type.tsToggle boolean state with ease:
import { useToggle } from '@/hooks';
const [isOpen, toggle] = useToggle(false);
<Button onClick={toggle}>Toggle</Button>Note: The enums in
src/enums/are provided as examples. Replace or extend them to fit your application's needs.
TypeScript enums are placed in the src/enums/ folder for better organization.
Define user roles with helper functions:
import { UserRole, isAdminRole } from '@/enums/user-role.enum';
const userRole = UserRole.ADMIN;
if (isAdminRole(userRole)) {
// Admin-specific logic
}Note: The store in
src/store/is provided as an example. Replace or extend it with your own stores as needed.
Client-side state is managed with Zustand stores in src/store/.
import { useCounterStore } from '@/store';
function CounterComponent() {
const { count, increment, decrement, reset } = useCounterStore();
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
<button onClick={reset}>Reset</button>
</div>
);
}- Zustand: Client-side state (UI state, local preferences, temporary data)
- TanStack Query: Server state (API data, caching, synchronization)
- Modules = Feature-specific components (used once)
- Components = Reusable components (used across features)
- Clearer separation of read vs write operations
- Easier to locate specific API calls
- Better organization as the app scales
- Signals "private/internal use only"
- Prevents accidental reuse of feature-specific components
- Keeps module boundaries clear
- Schemas = Runtime validation (Zod)
- Types = Compile-time types (TypeScript)
- Often both are needed for the same data structure
- β HTTP-only cookies for auth tokens
- β Zod validation on all forms
- β CSRF protection ready (extend API routes)
- β Middleware route protection
- β Type-safe API calls
1. Create docker-compose.yml:
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- JWT_SECRET=${JWT_SECRET}
- NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}
restart: unless-stopped2. Create .env file:
JWT_SECRET=your-secret-key
NEXT_PUBLIC_API_URL=http://localhost:3000/api3. Run:
docker-compose up -d
docker-compose logs -f # View logs
docker-compose down # StopIf you prefer more control or don't want to use Docker Compose:
Build the image:
docker build -t nextjs-template .Run the container:
# Basic
docker run -p 3000:3000 nextjs-template
# With environment file
docker run -p 3000:3000 --env-file .env nextjs-template
# Background mode
docker run -d -p 3000:3000 --name my-app nextjs-template
docker logs -f my-app- Install dependencies:
# Install Node.js 22
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt-get install -y nodejs
# Install pnpm
sudo npm install -g pnpm
# Install PM2 for process management
sudo npm install -g pm2- Clone and setup:
git clone <your-repo-url>
cd nextjs-template
pnpm install- Configure environment:
cp .env.example .env
nano .env # Edit with your values- Build the application:
pnpm build- Start with PM2:
pm2 start npm --name "nextjs-template" -- start
pm2 save
pm2 startup # Follow the instructions to enable startup on boot- Setup Nginx reverse proxy:
server {
listen 80;
server_name your-domain.com;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}- Enable site and reload Nginx:
sudo ln -s /etc/nginx/sites-available/nextjs-template /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx- Setup SSL with Certbot (optional):
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.comMake sure to set these in your deployment environment. See .env.example for the complete list of required variables.
With PM2:
pm2 logs nextjs-template # View logs
pm2 status # Check status
pm2 restart nextjs-template # Restart appWith Docker:
docker logs -f <container-id>
docker statsMIT