A high-performance, dynamic portfolio template built with Next.js 14, React Server Components, and Tailwind CSS. Designed to be fast, beautiful, and easy to manage.
- 🚀 Next.js 16 (App Router)
- ⚛️ React 19 (Server Components)
- 💅 Aceternity UI & Tailwind CSS for styling
- 📝 Markdown Blog with syntax highlighting
- 🔐 Admin Dashboard (CMS) included
- 💾 SQLite Database (via Prisma)
- 🐳 Docker Ready
The easiest way to get started is to use the pre-built Docker image.
-
Create a
docker-compose.ymlfile: Copy and paste the following content into a new file nameddocker-compose.yml:services: app: container_name: folio-app image: ghcr.io/59n/modern-folio:latest restart: always ports: - "3000:3000" environment: - DATABASE_URL=file:/app/data/dev.db - AUTH_SECRET=your_super_secret_auth_secret_change_me - NEXTAUTH_URL=http://localhost:3000 - AUTH_TRUST_HOST=true volumes: - db_data:/app/data - uploads_data:/app/public/uploads volumes: db_data: uploads_data:
-
Start the Application: Run this command in the same directory:
docker-compose up -d
The app will start at
http://localhost:3000.Note: In production, ensure you change the
AUTH_SECRETto a secure random string.
Use this method if you are developing features or modifying the code locally.
Create a .env file in the root directory:
DATABASE_URL="file:./dev.db"
AUTH_SECRET="super-secret-key-change-this"
NEXTAUTH_URL="http://localhost:3000"npm install
npm run devWhen you first run the application, visit /admin. A default admin account will be created if one doesn't exist:
- Email:
admin@example.com - Password: (Check your server console logs for the generated password on first run)
Important: Change your credentials immediately after logging in.
You can customize the site identity directly from the Admin > Settings page:
- Site Name & Description
- Colors & Themes
- Features (Enable/Disable Blog, Projects, etc.)
If you want to clear all data and start fresh (e.g. to remove old demo content):
docker-compose down -v
docker-compose up -d --buildNote: The -v flag deletes the persistent data volumes.
If you see JWTSessionError or "no matching decryption secret":
- Ensure
AUTH_SECRETis set in your environment. - Clear your browser cookies (specifically the
authjs.session-token). - This often happens when the
AUTH_SECRETchanges between deployments.