Personal logo
Personal logo
<blog/>
← Portfolio
Personal logo
Personal logo
<blog/>
← Portfolio
Personal logo
Personal logo
AboutProjectsSkillsTestimonialsExperienceBlogContact me
Design & Development by Hassan Umar Hassan
© 2026 All rights reserved
blog>Tutorial

From Setup to Production: Building Scalable Apps with Next.js and TypeScript.

Nabeel Hassan
January 15, 2025
8 min read
Next.jsTypeScriptReactWeb Development

Share this article

Spread the word with your network

Post on XShare on LinkedIn
Share

Why Next.js and TypeScript?

Web development has evolved significantly over the past few years. In this comprehensive guide, we'll explore how to build modern, scalable web applications using Next.js and TypeScript.

Next.js provides an excellent foundation for React applications with features like Server-Side Rendering (SSR) for better SEO, Static Site Generation (SSG) for optimal performance, API Routes for backend functionality, and Image Optimization out of the box.

TypeScript adds type safety and developer experience improvements that are crucial for large-scale applications.

Getting Started

First, let's create a new Next.js project with TypeScript:

bash
npx create-next-app@latest my-app --typescript --tailwind --eslint
cd my-app
npm run dev

This will set up a project with TypeScript configuration, Tailwind CSS for styling, and ESLint for code quality.

Project Structure

A well-organized project structure is crucial for maintainability:

project-structure.txt
1app/
2├── components/        # Reusable UI components
3├── hooks/            # Custom React hooks
4├── lib/              # Utility functions and configurations
5├── types/            # TypeScript type definitions
6└── (routes)/         # App Router pages

Key Features to Implement

Key features include the App Router for better performance and layouts, Server Components to reduce bundle size, Middleware for authentication and redirects, and API Routes for full-stack capabilities.

Advanced Patterns

Custom Hooks

Creating reusable logic with custom hooks:

use-local-storage.ts
1import { useState, useEffect } from 'react';
2
3export function useLocalStorage<T>(key: string, initialValue: T) {
4  const [storedValue, setStoredValue] = useState<T>(() => {
5    if (typeof window === 'undefined') {
6      return initialValue;
7    }
8    
9    try {
10      const item = window.localStorage.getItem(key);
11      return item ? JSON.parse(item) : initialValue;
12    } catch (error) {
13      console.error(`Error reading localStorage key "${key}":`, error);
14      return initialValue;
15    }
16  });
17
18  const setValue = (value: T | ((val: T) => T)) => {
19    try {
20      const valueToStore = value instanceof Function ? value(storedValue) : value;
21      setStoredValue(valueToStore);
22      if (typeof window !== 'undefined') {
23        window.localStorage.setItem(key, JSON.stringify(valueToStore));
24      }
25    } catch (error) {
26      console.error(`Error setting localStorage key "${key}":`, error);
27    }
28  };
29
30  return [storedValue, setValue] as const;
31}

Theme Provider

Implementing a dark mode theme provider:

theme-provider.tsx
1"use client";
2
3import { createContext, useContext, useEffect, useState } from 'react';
4
5type Theme = 'light' | 'dark' | 'system';
6
7interface ThemeContextProps {
8  theme: Theme;
9  setTheme: (theme: Theme) => void;
10}
11
12const ThemeContext = createContext<ThemeContextProps | undefined>(undefined);
13
14export function ThemeProvider({ children }: { children: React.ReactNode }) {
15  const [theme, setTheme] = useState<Theme>('system');
16
17  useEffect(() => {
18    const savedTheme = localStorage.getItem('theme') as Theme;
19    if (savedTheme) {
20      setTheme(savedTheme);
21    }
22  }, []);
23
24  useEffect(() => {
25    const root = window.document.documentElement;
26    const isDark = theme === 'dark' || 
27      (theme === 'system' && window.matchMedia('(prefers-color-scheme: dark)').matches);
28
29    if (isDark) {
30      root.classList.add('dark');
31    } else {
32      root.classList.remove('dark');
33    }
34
35    localStorage.setItem('theme', theme);
36  }, [theme]);
37
38  return (
39    <ThemeContext.Provider value={{ theme, setTheme }}>
40      {children}
41    </ThemeContext.Provider>
42  );
43}
44
45export function useTheme() {
46  const context = useContext(ThemeContext);
47  if (context === undefined) {
48    throw new Error('useTheme must be used within a ThemeProvider');
49  }
50  return context;
51}

Pro Tip: Always handle edge cases like SSR when working with browser APIs like localStorage or window.

Performance Optimization

Image Optimization

Next.js provides excellent image optimization out of the box:

hero-image.tsx
1import Image from 'next/image';
2
3export function HeroImage() {
4  return (
5    <Image
6      src="/hero-image.jpg"
7      alt="Hero section image"
8      width={1200}
9      height={600}
10      priority
11      className="rounded-lg"
12    />
13  );
14}

Code Splitting

Implement dynamic imports for better performance:

dashboard.tsx
1import { lazy, Suspense } from 'react';
2
3const DynamicChart = lazy(() => import('./Chart'));
4
5export function Dashboard() {
6  return (
7    <div>
8      <h1>Dashboard</h1>
9      <Suspense fallback={<div>Loading chart...</div>}>
10        <DynamicChart />
11      </Suspense>
12    </div>
13  );
14}

Deployment Considerations

When deploying your Next.js application: Use .env.local for development environment variables, enable compression and minification for build optimization, use Vercel or similar platforms for CDN integration, and consider serverless databases like PlanetScale for database setup.

For more advanced topics, check out the official Next.js documentation.

Happy coding! 🚀

Back to all posts