Skip to content

Instantly share code, notes, and snippets.

@kiranmaya
Last active October 20, 2025 06:05
Show Gist options
  • Save kiranmaya/2a04044c42fe173397abc8cfd34c418f to your computer and use it in GitHub Desktop.
Save kiranmaya/2a04044c42fe173397abc8cfd34c418f to your computer and use it in GitHub Desktop.
This comprehensive cheat sheet covers everything from basic programming concepts to advanced Next.js patterns used in the PaniPhonics project. Use it as a complete reference for understanding modern web development with React, Next.js, and TypeScript.

Complete Next.js & React Cheat Sheet - From Basics to Advanced

πŸ“š Table of Contents


πŸ“ Programming Fundamentals

Variables and Data Types

// Basic Variable Declarations
let name: string = "John";           // String
let age: number = 25;               // Number (integer)
let height: number = 5.9;           // Number (float)
let isStudent: boolean = true;      // Boolean
let scores: number[] = [85, 92, 78]; // Array
let person: object = { name: "John", age: 25 }; // Object

// Constants
const PI: number = 3.14159;
const MAX_SIZE: number = 100;

// Union Types
let value: string | number = "hello";
value = 42; // Valid

// Any Type (avoid when possible)
let data: any = "could be anything";

TypeScript Interfaces and Types

// Interface Definition
interface User {
  id: number;
  name: string;
  email: string;
  age?: number; // Optional property
  readonly createdAt: Date; // Read-only property
}

// Type Alias
type Status = 'active' | 'inactive' | 'pending';

type ApiResponse<T> = {
  success: boolean;
  data?: T;
  error?: string;
};

// Generic Interface
interface Repository<T> {
  findAll(): T[];
  findById(id: number): T | undefined;
  create(item: T): T;
  update(id: number, item: T): T | undefined;
  delete(id: number): boolean;
}

Control Structures

// Conditional Statements
if (age >= 18) {
  console.log("Adult");
} else if (age >= 13) {
  console.log("Teenager");
} else {
  console.log("Child");
}

// Switch Statement
switch (status) {
  case 'active':
    console.log("User is active");
    break;
  case 'inactive':
    console.log("User is inactive");
    break;
  default:
    console.log("Unknown status");
}

// Loops
for (let i = 0; i < scores.length; i++) {
  console.log(`Score ${i + 1}: ${scores[i]}`);
}

scores.forEach((score, index) => {
  console.log(`Score ${index + 1}: ${score}`);
});

while (count < 10) {
  count++;
}

do {
  count--;
} while (count > 0);

Functions

// Function Declaration
function add(a: number, b: number): number {
  return a + b;
}

// Arrow Function
const multiply = (a: number, b: number): number => a * b;

// Optional Parameters
function greet(name: string, greeting?: string): string {
  return `${greeting || 'Hello'}, ${name}!`;
}

// Default Parameters
function createUser(name: string, role: string = 'user'): User {
  return { id: Date.now(), name, email: '', createdAt: new Date() };
}

// Rest Parameters
function sum(...numbers: number[]): number {
  return numbers.reduce((total, num) => total + num, 0);
}

⚑ JavaScript/TypeScript Basics

ES6+ Features

// Destructuring
const user = { name: 'John', age: 25, email: '[email protected]' };
const { name, age } = user;

// Array Destructuring
const [first, second, ...rest] = scores;

// Spread Operator
const newUser = { ...user, role: 'admin' };
const allScores = [...scores, 95, 88];

// Template Literals
const message = `Hello, ${name}! You are ${age} years old.`;

// Promises
const fetchData = (): Promise<User[]> => {
  return new Promise((resolve, reject) => {
    // Async operation
    setTimeout(() => {
      resolve([{ id: 1, name: 'John', email: '[email protected]' }]);
    }, 1000);
  });
};

// Async/Await
const getUsers = async (): Promise<User[]> => {
  try {
    const users = await fetchData();
    return users;
  } catch (error) {
    console.error('Error fetching users:', error);
    throw error;
  }
};

Classes and Objects

// Class Definition
class Person {
  private id: number;
  protected name: string;
  public age: number;

  constructor(id: number, name: string, age: number) {
    this.id = id;
    this.name = name;
    this.age = age;
  }

  // Method
  greet(): string {
    return `Hello, I'm ${this.name}`;
  }

  // Getter
  get personId(): number {
    return this.id;
  }

  // Setter
  set personName(newName: string) {
    this.name = newName;
  }
}

// Inheritance
class Employee extends Person {
  private department: string;

  constructor(id: number, name: string, age: number, department: string) {
    super(id, name, age);
    this.department = department;
  }

  // Override method
  greet(): string {
    return `${super.greet()} and I work in ${this.department}`;
  }
}

Modules and Imports

// Export (lib/auth.ts)
export const authOptions = { /* ... */ };
export function connectToDatabase() { /* ... */ };

// Import
import { authOptions, connectToDatabase } from '../lib/auth';
import * as AuthModule from '../lib/auth';

// Default Export
export default function AuthProvider() { /* ... */ }

// Default Import
import AuthProvider from '../components/AuthProvider';

βš›οΈ React Fundamentals

JSX and Components

// Functional Component
const Welcome: React.FC<{ name: string }> = ({ name }) => {
  return <h1>Hello, {name}!</h1>;
};

// Arrow Function Component
const Button: React.FC<{ onClick: () => void; children: React.ReactNode }> = ({
  onClick,
  children
}) => (
  <button onClick={onClick} className="btn-primary">
    {children}
  </button>
);

// Class Component (Legacy)
class Counter extends React.Component<
  { initialCount?: number },
  { count: number }
> {
  constructor(props: { initialCount?: number }) {
    super(props);
    this.state = { count: props.initialCount || 0 };
  }

  increment = () => {
    this.setState({ count: this.state.count + 1 });
  };

  render() {
    return (
      <div>
        <p>Count: {this.state.count}</p>
        <button onClick={this.increment}>Increment</button>
      </div>
    );
  }
}

Props and State

// Props Interface
interface ProductCardProps {
  product: Product;
  onAddToCart?: (productId: string) => void;
  className?: string;
}

// Component with Props
const ProductCard: React.FC<ProductCardProps> = ({
  product,
  onAddToCart,
  className = ''
}) => {
  const [isHovered, setIsHovered] = React.useState(false);

  return (
    <div
      className={`product-card ${className} ${isHovered ? 'hovered' : ''}`}
      onMouseEnter={() => setIsHovered(true)}
      onMouseLeave={() => setIsHovered(false)}
    >
      <h3>{product.title}</h3>
      <p>&#x20B9;{product.price}</p>
      {onAddToCart && (
        <button onClick={() => onAddToCart(product.id!)}>
          Add to Cart
        </button>
      )}
    </div>
  );
};

React Hooks

// useState Hook
const [count, setCount] = React.useState<number>(0);
const [user, setUser] = React.useState<User | null>(null);

// useEffect Hook
React.useEffect(() => {
  // Component did mount
  fetchUser();

  // Cleanup function
  return () => {
    // Component will unmount
    console.log('Component unmounting');
  };
}, []); // Empty dependency array = run once

// useEffect with dependencies
React.useEffect(() => {
  // Run when user changes
  console.log('User updated:', user);
}, [user]);

// Custom Hook
const useLocalStorage = <T,>(key: string, initialValue: T) => {
  const [storedValue, setStoredValue] = React.useState<T>(() => {
    try {
      const item = window.localStorage.getItem(key);
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      return initialValue;
    }
  });

  const setValue = (value: T) => {
    try {
      setStoredValue(value);
      window.localStorage.setItem(key, JSON.stringify(value));
    } catch (error) {
      console.error(error);
    }
  };

  return [storedValue, setValue] as const;
};

Event Handling

// Event Handler Types
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
  event.preventDefault();
  console.log('Button clicked');
};

const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
  setValue(event.target.value);
};

const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
  event.preventDefault();
  // Form submission logic
};

// Keyboard Events
const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
  if (event.key === 'Enter') {
    handleSubmit();
  }
};

πŸš€ Next.js Framework

App Router Structure

// src/app/layout.tsx - Root Layout (Server Component)
import type { Metadata } from "next";
import { Geist, Ubuntu } from "next/font/google";

const geistSans = Geist({
  variable: "--font-geist-sans",
  subsets: ["latin"],
});

export const metadata: Metadata = {
  title: "PaniPonics",
  description: "Vertical Gardening towers for home-grown greens",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body className={`${geistSans.variable} ${ubuntu.variable} antialiased`}>
        <AuthProvider>
          <CartProvider>
            <AppShell>{children}</AppShell>
          </CartProvider>
        </AuthProvider>
      </body>
    </html>
  );
}

Pages and Routing

// src/app/page.tsx - Home Page
export default function HomePage() {
  return (
    <div>
      <Hero />
      <StatCards />
      <OurServices />
    </div>
  );
}

// src/app/store/page.tsx - Store Page
export default function StorePage() {
  return (
    <div className="container mx-auto">
      <h1>Our Products</h1>
      {/* Product listing */}
    </div>
  );
}

// src/app/store/[id]/page.tsx - Dynamic Product Page
interface ProductPageProps {
  params: { id: string };
}

export default function ProductPage({ params }: ProductPageProps) {
  return (
    <div>
      <h1>Product {params.id}</h1>
      {/* Product details */}
    </div>
  );
}

API Routes

// src/app/api/products/route.ts - REST API
import { NextRequest, NextResponse } from "next/server";

export async function GET(request: NextRequest) {
  try {
    const { searchParams } = new URL(request.url);
    const category = searchParams.get('category');

    // Fetch products logic
    const products = await getProducts(category);

    return NextResponse.json(products);
  } catch (error) {
    return NextResponse.json(
      { error: 'Failed to fetch products' },
      { status: 500 }
    );
  }
}

export async function POST(request: NextRequest) {
  try {
    const body = await request.json();

    // Validate required fields
    if (!body.title || !body.price) {
      return NextResponse.json(
        { error: 'Title and price are required' },
        { status: 400 }
      );
    }

    // Create product logic
    const product = await createProduct(body);

    return NextResponse.json(product, { status: 201 });
  } catch (error) {
    return NextResponse.json(
      { error: 'Failed to create product' },
      { status: 500 }
    );
  }
}

Server and Client Components

// Server Component (Default)
async function ProductList() {
  // This runs on the server
  const products = await fetchProducts();

  return (
    <div>
      {products.map(product => (
        <ProductCard key={product.id} product={product} />
      ))}
    </div>
  );
}

// Client Component
'use client';

import { useState } from 'react';

function InteractiveProductCard({ product }: { product: Product }) {
  const [isLiked, setIsLiked] = useState(false);

  // This runs on the client
  const handleLike = () => {
    setIsLiked(!isLiked);
    // API call to like product
  };

  return (
    <div>
      <ProductCard product={product} />
      <button onClick={handleLike}>
        {isLiked ? '&#x2764;&#xFE0F;' : '&#x1F90D;'} Like
      </button>
    </div>
  );
}

πŸ—οΈ Project Architecture

Component Architecture

// src/components/AppShell.tsx - Layout Wrapper
'use client';

import { useState } from 'react';
import SiteHeader from './SiteHeader';
import CartSidebar from './CartSidebar';
import AuthModal from './AuthModal';

export default function AppShell({ children }: { children: React.ReactNode }) {
  const [authOpen, setAuthOpen] = useState(false);

  return (
    <>
      <SiteHeader onOpenAuth={() => setAuthOpen(true)} />
      <CartSidebar />
      <main className="app-content">{children}</main>
      {authOpen && <AuthModal onClose={() => setAuthOpen(false)} />}
    </>
  );
}

Context Pattern

// src/context/CartContext.tsx - Global State Management
'use client';

import { createContext, useContext, useState, useEffect } from 'react';

type CartItem = Product & { qty: number };

type CartContextType = {
  items: CartItem[];
  addItem: (product: Product, qty?: number) => void;
  removeItem: (id: string) => void;
  clearCart: () => void;
  totalItems: () => number;
  totalAmount: () => number;
};

const CartContext = createContext<CartContextType | null>(null);

export function CartProvider({ children }: { children: React.ReactNode }) {
  const [items, setItems] = useState<CartItem[]>([]);

  // Load from localStorage
  useEffect(() => {
    const stored = localStorage.getItem('cart');
    if (stored) {
      setItems(JSON.parse(stored));
    }
  }, []);

  // Save to localStorage
  useEffect(() => {
    localStorage.setItem('cart', JSON.stringify(items));
  }, [items]);

  const addItem = (product: Product, qty = 1) => {
    setItems(current => {
      const existing = current.find(item => item.id === product.id);
      if (existing) {
        return current.map(item =>
          item.id === product.id
            ? { ...item, qty: item.qty + qty }
            : item
        );
      }
      return [...current, { ...product, qty }];
    });
  };

  return (
    <CartContext.Provider value={{
      items,
      addItem,
      removeItem: (id) => setItems(current => current.filter(item => item.id !== id)),
      clearCart: () => setItems([]),
      totalItems: () => items.reduce((sum, item) => sum + item.qty, 0),
      totalAmount: () => items.reduce((sum, item) => sum + (item.price * item.qty), 0)
    }}>
      {children}
    </CartContext.Provider>
  );
}

export function useCart() {
  const context = useContext(CartContext);
  if (!context) {
    throw new Error('useCart must be used within CartProvider');
  }
  return context;
}

Type Definitions

// src/types/products.ts - Product Types
export type Product = {
  id?: string;
  _id?: string;
  title?: string;
  heading?: string;
  description?: string;
  price?: number | string;
  amount?: number;
  images?: string[];
  img?: string;
  image?: string;
  imageUrl?: string;
  weight_kg?: number;
  freeShipping?: boolean;
  inventory?: number;
  createdAt?: string;
  [key: string]: any; // Allow additional fields
};

// Cart-specific types
export type CartProduct = Pick<Product,
  | 'id' | 'title' | 'price' | 'amount' | 'images'
  | 'img' | 'image' | 'imageUrl' | 'weight_kg'
  | 'freeShipping' | 'inventory'
>;

export type AddToCartProduct = CartProduct;

πŸ”§ State Management

React Context with Reducer

// Advanced Context with useReducer
type CartAction =
  | { type: 'ADD_ITEM'; payload: { product: Product; qty: number } }
  | { type: 'REMOVE_ITEM'; payload: { id: string } }
  | { type: 'UPDATE_QTY'; payload: { id: string; qty: number } }
  | { type: 'CLEAR_CART' };

const cartReducer = (state: CartState, action: CartAction): CartState => {
  switch (action.type) {
    case 'ADD_ITEM':
      // Implementation
      return newState;
    case 'REMOVE_ITEM':
      return { ...state, items: state.items.filter(item => item.id !== action.payload.id) };
    default:
      return state;
  }
};

export function CartProvider({ children }: { children: React.ReactNode }) {
  const [state, dispatch] = useReducer(cartReducer, initialState);

  return (
    <CartContext.Provider value={{ state, dispatch }}>
      {children}
    </CartContext.Provider>
  );
}

Custom Hooks

// src/hooks/useLocalStorage.ts
export function useLocalStorage<T>(key: string, initialValue: T) {
  const [storedValue, setStoredValue] = useState<T>(() => {
    try {
      const item = window.localStorage.getItem(key);
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      console.error('Error reading localStorage:', error);
      return initialValue;
    }
  });

  const setValue = useCallback((value: T | ((val: T) => T)) => {
    try {
      const valueToStore = value instanceof Function ? value(storedValue) : value;
      setStoredValue(valueToStore);
      window.localStorage.setItem(key, JSON.stringify(valueToStore));
    } catch (error) {
      console.error('Error setting localStorage:', error);
    }
  }, [key, storedValue]);

  return [storedValue, setValue] as const;
}

// src/hooks/useApi.ts
export function useApi<T>(url: string) {
  const [data, setData] = useState<T | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    const fetchData = async () => {
      try {
        setLoading(true);
        const response = await fetch(url);
        if (!response.ok) throw new Error('API Error');
        const result = await response.json();
        setData(result);
      } catch (err) {
        setError(err instanceof Error ? err.message : 'Unknown error');
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, [url]);

  return { data, loading, error };
}

πŸ› οΈ API Development

RESTful API Patterns

// src/app/api/products/route.ts - Complete CRUD API
import { NextRequest, NextResponse } from "next/server";
import { connectToDatabase } from "../../../lib/mongodb";
import { ObjectId } from "mongodb";

export async function GET(request: NextRequest) {
  try {
    const { db } = await connectToDatabase();
    const { searchParams } = new URL(request.url);

    const limit = parseInt(searchParams.get('limit') || '10');
    const skip = parseInt(searchParams.get('skip') || '0');
    const category = searchParams.get('category');

    let query = {};
    if (category) {
      query = { category };
    }

    const products = await db
      .collection("products")
      .find(query)
      .limit(limit)
      .skip(skip)
      .sort({ createdAt: -1 })
      .toArray();

    const total = await db.collection("products").countDocuments(query);

    return NextResponse.json({
      products,
      pagination: { total, limit, skip, hasMore: skip + limit < total }
    });
  } catch (error) {
    return NextResponse.json(
      { error: 'Failed to fetch products' },
      { status: 500 }
    );
  }
}

export async function POST(request: NextRequest) {
  try {
    const body = await request.json();
    const { db } = await connectToDatabase();

    // Validate required fields
    if (!body.title || !body.price) {
      return NextResponse.json(
        { error: 'Title and price are required' },
        { status: 400 }
      );
    }

    // Create product with timestamp
    const product = {
      ...body,
      createdAt: new Date().toISOString(),
      updatedAt: new Date().toISOString()
    };

    const result = await db.collection("products").insertOne(product);

    return NextResponse.json(
      { ...product, _id: result.insertedId },
      { status: 201 }
    );
  } catch (error) {
    return NextResponse.json(
      { error: 'Failed to create product' },
      { status: 500 }
    );
  }
}

Dynamic API Routes

// src/app/api/products/[id]/route.ts - Dynamic Route
import { NextRequest, NextResponse } from "next/server";
import { connectToDatabase } from "../../../../lib/mongodb";
import { ObjectId } from "mongodb";

interface RouteParams {
  params: { id: string };
}

export async function GET(request: NextRequest, { params }: RouteParams) {
  try {
    const { db } = await connectToDatabase();
    const { id } = params;

    let objectId;
    try {
      objectId = new ObjectId(id);
    } catch {
      return NextResponse.json(
        { error: 'Invalid product ID' },
        { status: 400 }
      );
    }

    const product = await db.collection("products").findOne({ _id: objectId });

    if (!product) {
      return NextResponse.json(
        { error: 'Product not found' },
        { status: 404 }
      );
    }

    return NextResponse.json(product);
  } catch (error) {
    return NextResponse.json(
      { error: 'Failed to fetch product' },
      { status: 500 }
    );
  }
}

export async function PUT(request: NextRequest, { params }: RouteParams) {
  try {
    const body = await request.json();
    const { db } = await connectToDatabase();
    const { id } = params;

    let objectId;
    try {
      objectId = new ObjectId(id);
    } catch {
      return NextResponse.json(
        { error: 'Invalid product ID' },
        { status: 400 }
      );
    }

    const updateData = {
      ...body,
      updatedAt: new Date().toISOString()
    };

    // Remove undefined fields
    Object.keys(updateData).forEach(key =>
      updateData[key] === undefined && delete updateData[key]
    );

    const result = await db.collection("products").updateOne(
      { _id: objectId },
      { $set: updateData }
    );

    if (result.matchedCount === 0) {
      return NextResponse.json(
        { error: 'Product not found' },
        { status: 404 }
      );
    }

    return NextResponse.json({ success: true });
  } catch (error) {
    return NextResponse.json(
      { error: 'Failed to update product' },
      { status: 500 }
    );
  }
}

export async function DELETE(request: NextRequest, { params }: RouteParams) {
  try {
    const { db } = await connectToDatabase();
    const { id } = params;

    let objectId;
    try {
      objectId = new ObjectId(id);
    } catch {
      return NextResponse.json(
        { error: 'Invalid product ID' },
        { status: 400 }
      );
    }

    const result = await db.collection("products").deleteOne({ _id: objectId });

    if (result.deletedCount === 0) {
      return NextResponse.json(
        { error: 'Product not found' },
        { status: 404 }
      );
    }

    return NextResponse.json({ success: true });
  } catch (error) {
    return NextResponse.json(
      { error: 'Failed to delete product' },
      { status: 500 }
    );
  }
}

πŸ—„οΈ Database Operations

MongoDB Integration

// src/lib/mongodb.ts - Database Connection
import { MongoClient, Db } from 'mongodb';

const MONGODB_URI = process.env.MONGODB_URI!;
const MONGODB_DB = process.env.MONGODB_DB!;

interface DatabaseConnection {
  client: MongoClient;
  db: Db;
}

let cachedClient: MongoClient | null = null;
let cachedDb: Db | null = null;

export async function connectToDatabase(): Promise<DatabaseConnection> {
  // Return cached connection if available
  if (cachedClient && cachedDb) {
    return { client: cachedClient, db: cachedDb };
  }

  try {
    // Create new connection
    const client = await MongoClient.connect(MONGODB_URI, {
      maxPoolSize: 10, // Connection pool size
      serverSelectionTimeoutMS: 5000, // Timeout for server selection
      socketTimeoutMS: 45000, // Socket timeout
    });

    const db = client.db(MONGODB_DB);

    // Cache the connection
    cachedClient = client;
    cachedDb = db;

    return { client, db };
  } catch (error) {
    console.error('Failed to connect to database:', error);
    throw new Error('Database connection failed');
  }
}

Database Query Patterns

// Complex queries and aggregations
export async function getProductAnalytics() {
  const { db } = await connectToDatabase();

  const pipeline = [
    {
      $group: {
        _id: '$category',
        totalProducts: { $sum: 1 },
        averagePrice: { $avg: '$price' },
        totalInventory: { $sum: '$inventory' },
        products: { $push: '$$ROOT' }
      }
    },
    {
      $sort: { totalProducts: -1 }
    }
  ];

  const results = await db.collection('products').aggregate(pipeline).toArray();
  return results;
}

// Transaction example
export async function transferInventory(
  fromProductId: string,
  toProductId: string,
  quantity: number
) {
  const { client, db } = await connectToDatabase();

  const session = client.startSession();

  try {
    await session.withTransaction(async () => {
      // Check if source has enough inventory
      const sourceProduct = await db.collection('products').findOne(
        { _id: new ObjectId(fromProductId), inventory: { $gte: quantity } },
        { session }
      );

      if (!sourceProduct) {
        throw new Error('Insufficient inventory');
      }

      // Update source inventory
      await db.collection('products').updateOne(
        { _id: new ObjectId(fromProductId) },
        { $inc: { inventory: -quantity } },
        { session }
      );

      // Update destination inventory
      await db.collection('products').updateOne(
        { _id: new ObjectId(toProductId) },
        { $inc: { inventory: quantity } },
        { session }
      );

      // Log the transfer
      await db.collection('inventory_transfers').insertOne({
        fromProductId,
        toProductId,
        quantity,
        timestamp: new Date()
      }, { session });
    });

    return { success: true };
  } catch (error) {
    return { success: false, error: error.message };
  } finally {
    await session.endSession();
  }
}

πŸ” Authentication

NextAuth Configuration

// src/lib/auth.ts - Complete Auth Setup
import type { AuthOptions, Session } from "next-auth";
import GoogleProvider from "next-auth/providers/google";
import CredentialsProvider from "next-auth/providers/credentials";
import { connectToDatabase } from "./mongodb";
import { scryptSync } from "crypto";

export const authOptions: AuthOptions = {
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    }),
    CredentialsProvider({
      name: "credentials",
      credentials: {
        email: { label: "Email", type: "email" },
        password: { label: "Password", type: "password" }
      },
      async authorize(credentials) {
        if (!credentials?.email || !credentials?.password) {
          return null;
        }

        try {
          const { db } = await connectToDatabase();
          const user = await db.collection("users").findOne({
            email: credentials.email
          });

          if (!user || !user.password) {
            return null;
          }

          // Verify password (using scrypt for security)
          const [salt, key] = user.password.split(":");
          const derivedKey = scryptSync(credentials.password, salt, 64);
          const derivedKeyHex = derivedKey.toString("hex");

          if (derivedKeyHex !== key) {
            return null;
          }

          return {
            id: user._id.toString(),
            email: user.email,
            name: user.name,
            image: user.image
          };
        } catch (error) {
          console.error("Authentication error:", error);
          return null;
        }
      }
    })
  ],
  session: {
    strategy: "jwt",
    maxAge: 30 * 24 * 60 * 60, // 30 days
  },
  secret: process.env.NEXTAUTH_SECRET,
  pages: {
    signIn: "/auth/signin",
    signUp: "/auth/signup",
    error: "/auth/error"
  },
  callbacks: {
    async jwt({ token, user }) {
      if (user) {
        token.id = user.id;
      }
      return token;
    },
    async session({ session, token }) {
      if (token) {
        session.user.id = token.id as string;
      }
      return session;
    },
    async signIn({ user, account, profile }) {
      if (account?.provider === "google") {
        try {
          const { db } = await connectToDatabase();

          // Upsert Google user
          await db.collection("users").updateOne(
            { email: user.email },
            {
              $set: {
                name: user.name,
                email: user.email,
                image: user.image,
                googleId: profile?.sub,
                updatedAt: new Date()
              }
            },
            { upsert: true }
          );
        } catch (error) {
          console.error("Error saving Google user:", error);
          return false;
        }
      }
      return true;
    }
  }
};

export default authOptions;

Auth Components

// src/components/AuthProvider.tsx
'use client';

import { SessionProvider } from 'next-auth/react';

export default function AuthProvider({
  children,
  session
}: {
  children: React.ReactNode;
  session?: any;
}) {
  return (
    <SessionProvider session={session}>
      {children}
    </SessionProvider>
  );
}

🎨 Styling & UI

Tailwind CSS Configuration

// tailwind.config.mjs
export default {
  content: [
    "./src/**/*.{js,ts,jsx,tsx,mdx}",
    "./app/**/*.{js,ts,jsx,tsx,mdx}",
    "./components/**/*.{js,ts,jsx,tsx,mdx}"
  ],
  theme: {
    extend: {
      colors: {
        primary: "var(--color-primary)",
        "primary-600": "var(--color-primary-600)",
        "primary-400": "var(--color-primary-400)",
        success: "var(--color-success)",
        warning: "var(--color-warning)",
        error: "var(--color-error)"
      },
      fontFamily: {
        sans: ["var(--font-geist-sans)", "system-ui", "sans-serif"],
        mono: ["var(--font-geist-mono)", "monospace"],
        ubuntu: ["var(--font-ubuntu)", "sans-serif"]
      },
      animation: {
        'fade-in': 'fadeIn 0.5s ease-in-out',
        'slide-up': 'slideUp 0.3s ease-out',
        'bounce-slow': 'bounce 2s infinite'
      }
    }
  },
  plugins: []
}

Responsive Design

// src/components/Hero.tsx - Responsive Component
'use client';

import { useState, useEffect } from 'react';

export default function Hero() {
  const [isExpanded, setIsExpanded] = useState(true);
  const [isMobile, setIsMobile] = useState(false);

  useEffect(() => {
    const checkMobile = () => {
      const mobile = window.innerWidth < 1024;
      setIsMobile(mobile);
      if (!mobile) {
        setIsExpanded(true);
      }
    };

    checkMobile();
    window.addEventListener('resize', checkMobile);

    return () => window.removeEventListener('resize', checkMobile);
  }, []);

  return (
    <section className="relative">
      <div className="max-w-6xl mx-auto flex flex-col lg:flex-row items-center gap-8">
        {/* Content */}
        <div className="lg:w-1/2">
          <h1 className="text-4xl md:text-5xl lg:text-6xl font-bold leading-tight">
            <span className="block bg-gradient-to-r from-cyan-500 to-blue-300 bg-clip-text text-transparent">
              Grow Fresh, Grow Smart
            </span>
          </h1>

          {/* Mobile expandable content */}
          <div className="lg:hidden">
            <div
              className={`transition-all duration-500 ease-out overflow-hidden ${
                isExpanded ? 'max-h-96 opacity-100' : 'max-h-20 opacity-90'
              }`}
            >
              <p>Content that expands on mobile...</p>
            </div>
            <button
              onClick={() => setIsExpanded(!isExpanded)}
              className="mt-2 text-primary-600 font-semibold"
            >
              {isExpanded ? 'Read less' : 'Read more'}
            </button>
          </div>
        </div>

        {/* Image */}
        <div className="lg:w-1/2">
          <div className="relative w-full max-w-md mx-auto">
            <Image
              src="/Images/hero.jpg"
              alt="PaniPonics Tower"
              width={400}
              height={400}
              className="w-full h-auto rounded-2xl shadow-2xl"
              priority
            />
          </div>
        </div>
      </div>
    </section>
  );
}

Animation with Framer Motion

// src/components/AnimatedSection.tsx
'use client';

import { motion } from 'framer-motion';
import { useInView } from 'react-intersection-observer';

interface AnimatedSectionProps {
  children: React.ReactNode;
  className?: string;
  delay?: number;
}

export default function AnimatedSection({
  children,
  className = '',
  delay = 0
}: AnimatedSectionProps) {
  const [ref, inView] = useInView({
    triggerOnce: true,
    threshold: 0.1
  });

  const variants = {
    hidden: { opacity: 0, y: 50 },
    visible: {
      opacity: 1,
      y: 0,
      transition: {
        duration: 0.6,
        delay,
        ease: "easeOut"
      }
    }
  };

  return (
    <motion.div
      ref={ref}
      initial="hidden"
      animate={inView ? "visible" : "hidden"}
      variants={variants}
      className={className}
    >
      {children}
    </motion.div>
  );
}

πŸš€ Performance Optimization

Image Optimization

// Next.js Image component patterns
import Image from 'next/image';

export default function OptimizedImages() {
  return (
    <div>
      {/* Local image with priority (above fold) */}
      <Image
        src="/Images/hero.jpg"
        alt="Hero image"
        width={800}
        height={600}
        priority // Loads immediately
        className="w-full h-auto"
      />

      {/* Remote image with loading optimization */}
      <Image
        src="https://example.com/image.jpg"
        alt="Remote image"
        width={400}
        height={300}
        loading="lazy" // Loads when near viewport
        placeholder="blur"
        blurDataURL="data:image/jpeg;base64,..."
      />

      {/* Responsive image */}
      <Image
        src="/Images/product.jpg"
        alt="Product"
        fill // Fills parent container
        className="object-cover"
        sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
      />
    </div>
  );
}

Code Splitting and Lazy Loading

// Dynamic imports for code splitting
const AdminPanel = lazy(() => import('../components/AdminPanel'));
const ChartComponent = lazy(() => import('../components/Chart'));

// Component with lazy loading
function Dashboard() {
  const [showChart, setShowChart] = useState(false);

  return (
    <div>
      <button onClick={() => setShowChart(true)}>
        Show Chart
      </button>

      {showChart && (
        <Suspense fallback={<div>Loading chart...</div>}>
          <ChartComponent />
        </Suspense>
      )}
    </div>
  );
}

Performance Monitoring

// src/lib/performance.ts
export function measurePerformance(name: string, fn: () => void | Promise<void>) {
  const start = performance.now();

  const result = fn();

  if (result instanceof Promise) {
    return result.finally(() => {
      const end = performance.now();
      console.log(`${name} took ${end - start} milliseconds`);
    });
  } else {
    const end = performance.now();
    console.log(`${name} took ${end - start} milliseconds`);
  }
}

// Usage
await measurePerformance('Database Query', async () => {
  const products = await fetchProducts();
  setProducts(products);
});

🚨 Error Handling

Error Boundaries

// src/components/ErrorBoundary.tsx
'use client';

import React from 'react';

interface ErrorBoundaryState {
  hasError: boolean;
  error?: Error;
}

interface ErrorBoundaryProps {
  children: React.ReactNode;
  fallback?: React.ComponentType<{ error?: Error; resetError: () => void }>;
}

class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
  constructor(props: ErrorBoundaryProps) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error: Error): ErrorBoundaryState {
    return { hasError: true, error };
  }

  componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
    console.error('Error caught by boundary:', error, errorInfo);

    // Report to error tracking service
    reportError(error, errorInfo);
  }

  resetError = () => {
    this.setState({ hasError: false, error: undefined });
  };

  render() {
    if (this.state.hasError) {
      const FallbackComponent = this.props.fallback || DefaultErrorFallback;
      return (
        <FallbackComponent
          error={this.state.error}
          resetError={this.resetError}
        />
      );
    }

    return this.props.children;
  }
}

function DefaultErrorFallback({ error, resetError }: { error?: Error; resetError: () => void }) {
  return (
    <div className="error-fallback">
      <h2>Something went wrong</h2>
      <p>{error?.message || 'An unexpected error occurred'}</p>
      <button onClick={resetError}>Try again</button>
    </div>
  );
}

API Error Handling

// src/lib/api-client.ts
export class ApiError extends Error {
  constructor(
    message: string,
    public status: number,
    public data?: any
  ) {
    super(message);
    this.name = 'ApiError';
  }
}

export async function apiRequest<T>(
  url: string,
  options: RequestInit = {}
): Promise<T> {
  try {
    const response = await fetch(url, {
      headers: {
        'Content-Type': 'application/json',
        ...options.headers
      },
      ...options
    });

    if (!response.ok) {
      let errorMessage = `HTTP ${response.status}: ${response.statusText}`;
      let errorData;

      try {
        errorData = await response.json();
        errorMessage = errorData.error || errorMessage;
      } catch {
        // Response is not JSON
      }

      throw new ApiError(errorMessage, response.status, errorData);
    }

    return await response.json();
  } catch (error) {
    if (error instanceof ApiError) {
      throw error;
    }

    // Network or other error
    throw new ApiError(
      error instanceof Error ? error.message : 'Network error',
      0
    );
  }
}

This comprehensive cheat sheet covers everything from basic programming concepts to advanced Next.js patterns used in the PaniPhonics project. Use it as a complete reference for understanding modern web development with React, Next.js, and TypeScript.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment