Tech Stack
This document describes the technologies used in this monorepo, why they were chosen, and trade-offs considered.
Core Framework
Next.js 13.2.4
Why Next.js?
- Full-stack capabilities — API routes, server-side rendering, static generation
- Excellent DX — Hot reloading, TypeScript support, excellent error messages
- Production-ready — Optimizations, image optimization, code splitting built-in
- Flexible routing — Supports both Pages Router (legacy) and App Router (modern)
Trade-offs:
- ✅ Supports both routing paradigms (migration path)
- ✅ Strong ecosystem and community
- ⚠️ Learning curve for App Router (newer paradigm)
- ⚠️ Some features still experimental (scrollRestoration, appDir)
Usage:
- App Router: Discart-me, QRACK (
/app)
- Pages Router: Personal website (
/pages)
Language & Type Safety
TypeScript 5.0.2
Why TypeScript?
- Type safety — Catch errors at compile time
- Better DX — IntelliSense, refactoring support
- Documentation — Types serve as inline documentation
- Maintainability — Easier to understand and modify code
Configuration:
- Strict mode enabled
- Path aliases configured (
@/, @/src/)
- Shared types across apps via
/src/types
Styling
Tailwind CSS 3.2.7
Why Tailwind?
- Utility-first — Rapid UI development
- Consistency — Design tokens enforced via classes
- Small bundle size — Unused styles purged
- No CSS-in-JS runtime — Better performance
Plugins Used:
@tailwindcss/forms — Form styling
@tailwindcss/typography — Blog prose styling
@tailwindcss/line-clamp — Text truncation
Custom Configuration:
- Custom color palette (zinc-based dark theme)
- Responsive breakpoints
- Custom utilities as needed
Trade-offs:
- ✅ Fast development
- ✅ Consistent design system
- ⚠️ Learning curve for utility classes
- ⚠️ Class names can get long (mitigated with components)
UI Components
Headless UI 1.7.13
Why Headless UI?
- Unstyled — Full control over styling
- Accessible — ARIA attributes built-in
- Small bundle — Only what we need
- React-first — Built for React
Usage: Modal dialogs, dropdowns, accessibility primitives
Radix UI 1.0.1
Why Radix UI?
- Accessible — WCAG compliant components
- Unstyled — Customizable with Tailwind
- Feature-rich — Complex UI primitives
Usage: Advanced UI components (limited usage)
State Management
React Context API
Why Context API?
- Built-in — No external dependencies
- Simple — Perfect for auth state, theme
- Sufficient — Covers current needs (no complex state)
Usage:
- Authentication state (Discart-me, QRACK, BoxHub)
- User preferences (future)
React Query (@tanstack/react-query) 5.90.11
Why React Query?
- Server state — Caching, refetching, background updates
- Optimistic updates — Better UX
- DevTools — Excellent debugging
Usage:
- Comments and ratings features
- Will expand to more features
Trade-offs:
- ✅ Excellent for server state
- ⚠️ Overkill for simple client state (use Context instead)
Data Fetching
Fetch API (Native)
Why Fetch?
- Native — No dependencies
- Modern — Promise-based, async/await
- Sufficient — Covers REST and GraphQL needs
Usage:
- REST API calls (Discart-me)
- GraphQL requests (via shared
graphqlClient.ts)
Trade-offs:
- ✅ No external dependencies
- ⚠️ Less features than Axios (handled via wrappers)
GraphQL
Custom GraphQL Client
Why Custom Client?
- Lightweight — No Apollo/Relay overhead
- Simple — Just what we need
- Flexible — Easy to customize
Implementation: src/lib/api/core/graphqlClient.ts
Features:
- Type-safe queries (via TypeScript)
- Error handling
- Authorization headers
- Logging (development)
Trade-offs:
- ✅ No heavy framework
- ✅ Full control
- ⚠️ Manual query construction
- ⚠️ No caching (handled by React Query where needed)
Image Processing
Canvas API (Browser Native)
Why Canvas API?
- No dependencies — Built into browsers
- Powerful — Full control over image manipulation
- Fast — Native performance
Implementation: src/lib/image/processImage.ts
Usage:
- Resize images before upload
- Compress images for mobile
- Format conversion (JPEG, PNG, WebP)
Trade-offs:
- ✅ Zero dependencies
- ✅ Works offline
- ⚠️ Client-side only (no SSR)
QR Code Generation
qrcode 1.5.4 & qrcode.react 4.2.0
Why These Libraries?
- qrcode — Server-side generation
- qrcode.react — React component for display
- Small bundle — Lightweight
- Well-maintained — Active community
Usage: QRACK container QR code generation
html5-qrcode 2.3.8
Why html5-qrcode?
- QR scanning — Mobile camera support
- Modern API — Promise-based
- Active development — Well-maintained
Usage: QRACK scanner page
Native HTML5 Validation
Why Native?
- No dependencies — Built-in browser support
- Accessible — Screen reader support
- Simple — Sufficient for current needs
Future Consideration: Form libraries (React Hook Form, Formik) if forms become complex
Markdown & Content
MDX (@mdx-js/loader 2.3.0)
Why MDX?
- React in Markdown — Interactive blog posts
- Type-safe — TypeScript support
- Flexible — Custom components in markdown
Usage: Blog posts (/pages/blogs)
Why GFM?
- GitHub Flavored Markdown — Tables, task lists, etc.
- Standard — Familiar syntax
rehype-prism (@mapbox/rehype-prism 0.8.0)
Why Prism?
- Syntax highlighting — Code blocks in blog
- Lightweight — Smaller than highlight.js
Date Handling
date-fns 4.1.0
Why date-fns?
- Tree-shakeable — Only import what you use
- Immutable — No mutation
- TypeScript — Full type support
- Smaller bundle — Than Moment.js
Why Not Moment.js?
- Moment.js is in maintenance mode
- Larger bundle size
- Mutable (can cause bugs)
PDF Generation
@react-pdf/renderer 4.3.1
Why React PDF?
- React-based — Familiar API
- Server-side — Generate PDFs in API routes
- TypeScript — Type-safe
Usage: Resume PDF generation (/pages/api/generate-resume.ts)
Email
nodemailer 6.9.16
Why Nodemailer?
- Mature — Widely used
- Flexible — SMTP, Gmail, SendGrid, etc.
- Simple API — Easy to use
Usage: Contact form email sending (/pages/api/send-email.tsx)
Animation
framer-motion 10.6.0
Why Framer Motion?
- Declarative — Easy to use
- Performant — GPU-accelerated
- Flexible — Complex animations
Usage: Page transitions, hero animations (limited usage)
Trade-offs:
- ✅ Great DX
- ⚠️ Larger bundle (used sparingly)
Notifications
sonner 2.0.7
Why Sonner?
- Lightweight — Small bundle
- Accessible — ARIA support
- Customizable — Easy to style
Usage: Toast notifications (success, error messages)
Utilities
clsx 1.2.1 & tailwind-merge 1.10.0
Why These?
- clsx — Conditional class names
- tailwind-merge — Merge Tailwind classes intelligently
- Small — Minimal bundle impact
Usage: Dynamic styling in components
ESLint
Why ESLint?
- Code quality — Catch errors early
- Consistency — Enforce patterns
- Next.js config — Built-in rules
Build & Deployment
Next.js Built-in
Why Next.js Build?
- Optimized — Automatic code splitting
- Fast — Incremental builds
- Production-ready — Optimizations enabled
Deployment: Vercel (recommended) or any Node.js host
Future Considerations
Potential Additions
- Testing
- Vitest or Jest for unit tests
- React Testing Library for components
- Playwright for E2E tests
- State Management
- Zustand (if Context becomes unwieldy)
- Jotai (atomic state)
- Forms
- React Hook Form (if forms become complex)
- Zod (schema validation)
- Monitoring
- Sentry (error tracking)
- Analytics (Plausible, Vercel Analytics)
Bundle Size Considerations
Current Strategy:
- Code splitting by route (automatic)
- Dynamic imports for heavy dependencies
- Tree-shaking enabled
- Image optimization via Next.js Image
Monitoring:
- Next.js build output shows bundle sizes
- Future: Bundle analyzer for detailed breakdown