Real World Example

How Teams Organise a Next.js Repo

A typical structure used by product teams (for example a D2C brand store or an edtech course site) keeps routes thin and logic in lib/:

shopease/
├── app/
│   ├── layout.tsx
│   ├── page.tsx                 /
│   ├── products/
│   │   ├── page.tsx             /products
│   │   └── [id]/page.tsx        /products/42
│   ├── cart/page.tsx            /cart
│   └── api/health/route.ts      GET /api/health
├── components/  Navbar.tsx  ProductCard.tsx  AddToCartButton.tsx
├── lib/         db.ts  products.ts  format.ts
├── types/       product.ts
├── public/      logo.svg  banner.webp
├── .env.local
└── next.config.ts

A Shared Formatter

Code in lib/ is plain TypeScript, so you can test it on its own. Indian shops format prices with lakh/crore grouping:

Example

lib/format.ts as a runnable program:

export function formatPrice(rupees: number): string {
  return new Intl.NumberFormat('en-IN', {
    style: 'currency',
    currency: 'INR',
    maximumFractionDigits: 0,
  }).format(rupees);
}

export function discountPercent(mrp: number, price: number): string {
  return Math.round(((mrp - price) / mrp) * 100) + '% off';
}

console.log(formatPrice(799));
console.log(formatPrice(124999));
console.log(formatPrice(1250000));
console.log(discountPercent(1999, 1299));

Output

₹799
₹1,24,999
₹12,50,000
35% off

Exercise: Add a lib/format.ts function formatDate(iso: string) that prints dates like 15 Jan 2026.

Show answer
export function formatDate(iso: string): string {
  return new Intl.DateTimeFormat('en-IN', {
    day: 'numeric',
    month: 'short',
    year: 'numeric',
    timeZone: 'Asia/Kolkata',
  }).format(new Date(iso));
}