A full-stack technical blog platform with a Next.js web app and an Expo mobile app, managed as a pnpm workspace.
The web app delivers an immersive landing experience with 3D visuals, a public blog, GitHub authentication, and a MongoDB-backed admin dashboard. The mobile app reads from the same REST API and renders posts with NativeWind styling.
Live demo: kirito-blog.vercel.app
| Light theme | Dark theme |
|
|
The landing page features a displacement-sphere 3D hero, GSAP-powered intro animations, custom stencil typography, and a system-aware light/dark theme toggle.
- Immersive web experience — Three.js displacement sphere, scroll animations, ambient audio, and route transitions
- Full blog platform — Category filters, markdown posts with syntax highlighting, SEO metadata, and static generation for published slugs
- GitHub authentication — NextAuth.js v5 sign-in with user sessions stored in MongoDB
- Admin dashboard — Create, edit, review, publish, and draft articles; manage users and view platform stats
- Cross-platform mobile reader — Expo app for iOS, Android, and web that consumes the deployed API
- Production-ready tooling — Shared ESLint flat config, Prettier, Husky pre-commit hooks, and GitHub Actions CI
| Package | Path | Description |
|---|---|---|
| Web | web/ |
Next.js 16 App Router site — landing page, blog, admin panel, REST API |
| Mobile | mobile/ |
Expo Router app — blog reader for iOS, Android, and web |
- Next.js 16 (App Router, Turbopack)
- React 19
- Tailwind CSS 4
- MongoDB + Zod validation
- NextAuth.js v5 (GitHub provider)
- Three.js / React Three Fiber for 3D visuals
- GSAP for animations
- Shiki + react-markdown for code blocks
- Expo 54 + Expo Router
- React Native 0.81
- NativeWind 4
- Markdown rendering with syntax highlighting (Shiki)
- pnpm workspaces
- ESLint 9 flat config (shared base + per-package overrides)
- Prettier + Husky pre-commit hooks
flowchart LR
subgraph clients [Clients]
Web[Next.js Web App]
Mobile[Expo Mobile App]
end
subgraph api [API Layer]
REST["/api/blogs · /api/auth"]
end
subgraph data [Data]
MongoDB[(MongoDB Atlas)]
end
Web --> REST
Mobile --> REST
REST --> MongoDB
| Route / area | Purpose |
|---|---|
/ |
Animated landing page with 3D hero |
/blogs |
Blog listing with category filters |
/blogs/[slug] |
Individual post (markdown + syntax highlight) |
/about |
About page with timeline and skills grid |
/admin |
Dashboard — stats, quick actions |
/admin/blogs |
Article management (CRUD, publish/draft) |
/admin/users |
User management |
/api/blogs |
REST endpoints for blog data |
/api/auth/* |
NextAuth GitHub OAuth |
blog/
├── Demo/ # Screenshots for README and docs
├── eslint.config.mjs # Shared ESLint base (imported by web & mobile)
├── package.json # Root scripts: lint, format, husky
├── pnpm-workspace.yaml
├── web/
│ ├── src/app/ # Next.js routes (public, admin, API)
│ ├── src/components/ # UI, 3D, theme, animations
│ ├── src/db/ # MongoDB connection & services
│ └── eslint.config.mjs # Next.js + shared base rules
└── mobile/
├── app/ # Expo Router screens
├── api/ # Axios client for web API
└── eslint.config.mjs # Expo + shared base rules
- Node.js 20+
- pnpm 10+
- MongoDB Atlas (or local MongoDB) for the web app
- GitHub OAuth app for authentication
- Expo Go or a simulator/emulator for mobile development
git clone https://github.com/<your-username>/blog.git
cd blog
pnpm installCopy the example env file and fill in your values:
cp web/.env.example web/.env.localOr create web/.env.local manually:
# MongoDB
MONGODB_URI=mongodb+srv://<user>:<password>@<cluster>.mongodb.net/?retryWrites=true&w=majority
# NextAuth / Auth.js
AUTH_SECRET=your-random-secret-at-least-32-chars
AUTH_GITHUB_ID=your-github-oauth-client-id
AUTH_GITHUB_SECRET=your-github-oauth-client-secret
NEXTAUTH_URL=http://localhost:4000Generate AUTH_SECRET:
openssl rand -base64 32GitHub OAuth setup: Create an OAuth app at github.com/settings/developers with callback URL http://localhost:4000/api/auth/callback/github (use your production URL when deploying).
pnpm --dir web devOpen http://localhost:4000.
pnpm --dir mobile startThe mobile API client points to the deployed web API by default (mobile/api/client.ts). Update baseURL to http://localhost:4000/api/ when testing against a local web server.
Run these from the repository root unless noted.
| Command | Description |
|---|---|
pnpm lint |
Lint root, web, and mobile |
pnpm lint:web |
Lint web only |
pnpm lint:mobile |
Lint mobile only |
pnpm lint:fix |
Auto-fix lint issues across the monorepo |
pnpm format |
Format all files with Prettier |
pnpm format:check |
Check formatting without writing |
pnpm --dir web dev |
Start Next.js dev server (port 4000) |
pnpm --dir web build |
Production build (uses cross-env NODE_ENV=production) |
pnpm --dir web start |
Start production server |
pnpm --dir mobile start |
Start Expo dev server |
pnpm --dir mobile android |
Open on Android emulator |
pnpm --dir mobile ios |
Open on iOS simulator |
- Animated landing page with displacement-sphere 3D hero and intro loader
- Blog listing with category filters and card layout
- Slug-based detail pages with markdown rendering and Shiki syntax highlighting
- About page with journey timeline, skills constellation, and bento grid
- Light/dark theme with system preference support
- Ambient background music and hover sound effects
- GitHub sign-in via NextAuth; users persisted in MongoDB
- Admin panel at
/admin— dashboard stats, user management, blog CRUD - Draft and published workflow for articles
- API routes:
/api/blogs,/api/blogs/[slug],/api/auth/[...nextauth]
- Blog slugs pre-rendered via
generateStaticParams - Admin routes use
export const dynamic = 'force-dynamic'so they are not pre-rendered at build time and do not require a live database duringnext build
The Expo mobile app provides a minimal, dark-themed reader for blog content:
- Home screen with branding and navigation to the blog list
- Fetches posts from the web API (
https://kirito-blog.vercel.app/api/by default) - Markdown rendering with syntax-highlighted code blocks
- Runs on iOS, Android, and web via Expo
ESLint is configured as a shared flat config:
- Root
eslint.config.mjs— shared TypeScript, Prettier, and React Hooks rules - Web
web/eslint.config.mjs— extends base +eslint-config-next - Mobile
mobile/eslint.config.mjs— extends base +eslint-config-expo
Web and mobile are linted from their own directories so framework plugins resolve paths correctly.
GitHub Actions (.github/workflows/ci.yml) runs on push/PR to main:
pnpm lintpnpm format:checkpnpm --dir web build(with auth and MongoDB secrets)pnpm --dir mobile tsc --noEmit
Required GitHub secrets for the web build:
MONGODB_URIAUTH_SECRETAUTH_GITHUB_IDAUTH_GITHUB_SECRETNEXTAUTH_URL
The web app is designed for Vercel deployment. Set the same environment variables from .env.local in your Vercel project settings, and update the GitHub OAuth callback URL to match your production domain.
For the mobile app, point mobile/api/client.ts baseURL at your deployed API before building for production.
The web build script uses cross-env NODE_ENV=production so production builds succeed even if your shell has NODE_ENV=development set globally (common on Windows). Next.js expects NODE_ENV=production during next build.
If you see MongoDB connection warnings during build for public blog pages, ensure Atlas network access allows your IP or use dummy/fallback data for static generation. Admin pages are already excluded from static prerender.
This project is licensed under the MIT License.

