A modern, full-stack starter template powered by PayloadCMS and Next.js. Built with performance and developer experience in mind.
- Next.js 15 with App Router
- PayloadCMS for content management
- PostgreSQL database with Neon
- AWS S3 for media storage
- Authentication with Server Actions & JWT
- Tailwind CSS & shadcn/ui for styling
- Responsive & Mobile-First design
- TypeScript for type safety
- brijr/craft utilities included
Before you begin, ensure you have:
- Node.js (^18.20.2 or >=20.9.0)
- pnpm package manager
- PostgreSQL database (local or cloud)
- AWS S3 bucket for media storage
- Basic knowledge of TypeScript and React
- Clone and install dependencies:
git clone https://github.com/9d8dev/pay.git cd pay pnpm install- Set up your environment:
cp .env.example .env- Configure your
.envfile with:
# Database ConfigurationDATABASE_URI=your_postgres_connection_stringPAYLOAD_SECRET=your_secure_secret_key# S3 ConfigurationS3_BUCKET=your_bucket_nameS3_REGION=your_bucket_regionS3_ACCESS_KEY_ID=your_access_keyS3_SECRET_ACCESS_KEY=your_secret_key- Start the development server:
pnpm dev- Visit http://localhost:3000/admin to create your first admin user
Local PostgreSQL:
- Install PostgreSQL on your machine
- Create a new database
- Use the connection string:
postgresql://username:password@localhost:5432/database
Cloud PostgreSQL (Recommended):
- Create a database on Neon
- Copy the connection string from your dashboard
- Enable SSL mode with
?sslmode=require
AWS S3 Configuration (Guide)
Create an S3 bucket:
- Go to AWS Console > S3
- Create a new bucket
- Enable public access settings
- Configure CORS for your domain
Create IAM credentials:
- Create a new IAM user
- Attach
AmazonS3FullAccesspolicy - Save the access key and secret
Configure bucket policy:
{"Version": "2012-10-17", "Statement": [{"Sid": "PublicReadGetObject", "Effect": "Allow", "Principal": "*", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::your-bucket-name/*" } ] }pay/ ├── src/ │ ├── app/ # Next.js app router │ ├── collections/ # PayloadCMS collections │ ├── components/ # React components │ └── lib/ # Utility functions ├── public/ # Static assets └── package.json # Dependencies pnpm dev- Start development serverpnpm build- Build for productionpnpm start- Start production serverpnpm generate:types- Generate PayloadCMS types
- Push your code to GitHub
- Import project to Vercel
- Configure environment variables
- Deploy!
- Use PostgreSQL (We recommend Neon, or any other cloud provider with PostgreSQL support)
- Enable serverless pooling
- Configure connection pooling
We welcome contributions! Please:
- Fork the repository
- Create a feature branch
- Submit a pull request
MIT 9d8
- Server-side authentication using Next.js Server Actions
- JWT-based session management with secure HTTP-only cookies
- Built-in login, register, and logout functionality
- Protected routes with automatic redirects
- TypeScript types for auth responses
- Pre-build App Users collection at
collections/AppUsers.ts
- Login Form tsx import{LoginForm } from '@/components/auth/login-form'
export default function LoginPage(){return }
2. **Register Form** ```tsx import{RegisterForm } from '@/components/auth/register-form' export default function RegisterPage(){return <RegisterForm />} - Logout Button
import{LogoutButton}from'@/components/auth/logout-button'exportdefaultfunctionNavBar(){return<LogoutButton/>}- Protected Routes Create a layout.tsx in your protected route directory:
import{redirect}from'next/navigation'import{getUser}from'@/lib/actions/auth'import{cookies}from'next/headers'exportdefaultasyncfunctionProtectedLayout({ children }){constcookieStore=awaitcookies()consttoken=cookieStore.get('payload-token')if(!token){redirect('/login')}constuser=awaitgetUser()if(!user){redirect('/login')}return<>{children}</>}The following server actions are available in @/lib/actions/auth:
loginUser({email, password })- Authenticates user and sets sessionregisterUser({email, password })- Creates new user and logs them inlogoutUser()- Ends session and redirects to homegetUser()- Returns current authenticated user or null
