Nikhil Rajput

July 5, 2026 · View on GitHub

Personal portfolio website for Nikhil Rajput, rebuilt from scratch on the modern Next.js 16 stack with a database-driven content layer, GitHub OAuth admin panel, and full CI/test suite.

Stars Forks Watchers Contributors

GitHub last commit GitHub issues GitHub pull requests GitHub License


Screenshots

An immersive, cursor-reactive hero over a page-wide gradient background, with a first-class light and dark theme.

Dark themeLight theme
Home page, dark themeHome page, light theme

Table of Contents


Stack

LayerTechnology
FrameworkNext.js 16 (App Router, Turbopack)
UI libraryReact 19
LanguageTypeScript
StylingTailwind CSS v4, Geist font
AnimationFramer Motion
IconsLucide React
DatabasePostgreSQL via postgres-js
ORMDrizzle ORM
AuthAuth.js v5 — GitHub OAuth
File storageVercel Blob
EmailResend
AnalyticsVercel Analytics + Google Analytics
Package managerBun
Unit testsVitest + Testing Library
E2E testsPlaywright
DeploymentVercel

Architecture overview

  • DB-driven content — profile, projects, experiences, skills, services, social links, taglines, and FAQs are stored in PostgreSQL and seeded via bun run db:seed. The admin panel allows live CRUD editing. The profile drives the hero (name, editable role marquee, "Currently" tagline, avatar) and the About section.
  • Image uploads — the profile avatar and skill icons are uploaded from the admin panel to Vercel Blob (SVG stored raw for crisp vector icons, raster formats optimized to WebP) and served from the CDN. Each field also accepts a plain URL, and existing static asset paths keep working.
  • GitHub cache — project metadata (stars, forks, descriptions) is fetched from the GitHub API at build time and revalidated server-side via GITHUB_TOKEN. Only repos listed in the database are enriched.
  • Testimonials — visitors submit testimonials through a validated form (inline per-field errors) with an optional cropped avatar uploaded to Vercel Blob. Submissions land in a moderation queue; the admin approves or rejects them before they appear publicly. Resend emails the admin on new submissions.
  • Admin panel — protected by GitHub OAuth (AUTH_GITHUB_ID / AUTH_GITHUB_SECRET). Access is restricted to the GitHub login set in ADMIN_GITHUB_LOGIN (default: nixrajput). The panel exposes CRUD tabs for all content types plus the testimonial moderation queue.
  • SEO / GEO — structured metadata, Open Graph, Twitter cards, Google verification, and robots.txt are generated from the database profile row and site config.

Prerequisites

  • Bun ≥ 1.1 — the only supported package manager and runtime.
  • PostgreSQL ≥ 14 — running locally or via a managed service (Neon, Supabase, etc.).
  • A GitHub OAuth App for admin login (Settings → Developer settings → OAuth Apps).

Local setup

1. Clone and install

git clone https://github.com/nixrajput/portfolio-nextjs.git
cd portfolio-nextjs
bun install

2. Environment variables

Copy .env.example to .env.local and fill in the values:

cp .env.example .env.local
VariableRequiredDescription
DATABASE_URLYesPostgreSQL connection string, e.g. postgres://user:pass@localhost:5432/portfolio
AUTH_SECRETYesRandom secret for Auth.js session encryption (openssl rand -base64 32)
AUTH_GITHUB_IDYesGitHub OAuth App client ID
AUTH_GITHUB_SECRETYesGitHub OAuth App client secret
ADMIN_GITHUB_LOGINYesGitHub username allowed to access the admin panel (e.g. nixrajput)
GITHUB_TOKENRecommendedGitHub personal access token for project metadata fetching (higher rate limits)
BLOB_READ_WRITE_TOKENYes (prod)Vercel Blob token for image uploads (avatar, skill icons, testimonial avatars)
RESEND_API_KEYYes (prod)Resend API key for admin notification emails
RESEND_FROM_EMAILYes (prod)From address for emails, e.g. Portfolio <noreply@nixrajput.com> (verified domain)
CONTACT_EMAILYes (prod)Email address to receive testimonial submission notifications
REVALIDATE_SECRETYes (prod)Secret for the /api/revalidate on-demand revalidation endpoint
NEXT_PUBLIC_SITE_URLYesCanonical site URL, e.g. https://nixrajput.com
NEXT_PUBLIC_GTAG_IDOptionalGoogle Analytics measurement ID (e.g. G-XXXXXXXXXX)
NEXT_PUBLIC_GOOGLE_VERIFICATION_TOKENOptionalGoogle Search Console site verification token

The resume link is stored in the database (profile.resumeUrl) and managed from the admin panel — not via an environment variable.

3. Database setup

Run migrations to create the schema, then seed initial data:

bunx drizzle-kit migrate
bun run db:seed

4. Git hooks

Enable the pre-push lint + format gate once per clone:

git config core.hooksPath .githooks

This runs bun run lint and bun run format:check before every push. Bypass in an emergency with git push --no-verify.

5. Run the dev server

bun run dev

The site is available at http://localhost:4000.


Commands

CommandDescription
bun run devStart dev server (Turbopack, port 4000)
bun run buildProduction build (Turbopack)
bun run startStart production server
bun run lintRun ESLint
bun run lint:fixRun ESLint with auto-fix
bun run formatFormat source files with Prettier
bun run format:checkCheck formatting without writing
bun run typecheckRun tsc --noEmit
bun run testRun Vitest unit tests
bun run test:watchRun Vitest in watch mode
bun run test:e2eRun Playwright end-to-end tests
bun run db:generateGenerate a new Drizzle migration
bun run db:migrateApply pending migrations
bun run db:pushPush schema changes directly (dev only)
bun run db:studioOpen Drizzle Studio
bun run db:seedSeed the database with initial data

Admin panel

The admin panel is at /admin and requires authentication via GitHub OAuth.

  • Only the GitHub account set in ADMIN_GITHUB_LOGIN (e.g. nixrajput) can sign in.
  • After signing in you have access to CRUD tabs for profile, projects, experiences, skills, services, social links, taglines, FAQs, and funding links.
  • The profile editor manages your name, bio, hero role marquee, "Currently" tagline, resume link, and avatar (upload or URL). Skill icons are uploaded or URL-pasted per skill.
  • Testimonial submissions appear in a moderation queue. Approve a testimonial to make it visible on the site; reject to discard it.

Image uploads (avatar, skill icons) require BLOB_READ_WRITE_TOKEN to be set.


Deployment

The site is deployed on Vercel. The main branch deploys automatically.

  1. Import the repository in Vercel.
  2. Add all required environment variables in the Vercel project settings.
  3. Provision a PostgreSQL database (Vercel Postgres / Neon) and set DATABASE_URL to its pooled connection string.
  4. Seed the production database once by hand against the production DATABASE_URL: DATABASE_URL=<prod> bun run db:seed. The seed is guarded (seed-if-empty), so it inserts the initial content only when the database is empty and never overwrites later edits.
  5. Push to master — Vercel runs the vercel-build script, which applies pending migrations and then builds. It does not seed; data seeding is the one-time manual step above, and further content changes are made in the admin panel.

The local DATABASE_URL stays pointed at your local Postgres for development; only the Vercel environment uses the production database.


Contributing

See CONTRIBUTING.md for the full guide.


License

MIT — see LICENSE.


Sponsor ko-fi "Buy Me A Coffee"


Connect with me

GitHub: nixrajput LinkedIn: nixrajput Instagram: nixrajput X: nixrajput