Next.js & TypeScript: A Beginner’s Guide to Building Web Apps

In the ever-evolving landscape of web development, staying current with the latest technologies is crucial. Next.js, a powerful React framework, has gained immense popularity for its ability to simplify building modern web applications. Coupled with TypeScript, a superset of JavaScript that adds static typing, you can create robust, scalable, and maintainable applications. This tutorial will guide you through the process of setting up a Next.js project with TypeScript, explaining key concepts with practical examples, and providing step-by-step instructions to get you started.

Why Choose Next.js with TypeScript?

Before diving into the code, let’s understand why Next.js and TypeScript are a winning combination:

  • Next.js: Offers features like server-side rendering (SSR), static site generation (SSG), and optimized routing, making it excellent for performance and SEO.
  • TypeScript: Catches type-related errors during development, improving code quality and making refactoring easier. It provides better autocompletion and code navigation in your IDE.

Together, they provide a developer-friendly experience and help you build applications that are both performant and maintainable.

Setting Up Your Development Environment

Before we start, you’ll need the following installed on your system:

  • Node.js and npm (or yarn/pnpm): These are essential for managing project dependencies and running the development server.
  • A code editor: Visual Studio Code (VS Code) is highly recommended due to its excellent TypeScript support.

Let’s create a new Next.js project with TypeScript. Open your terminal and run the following command:

npx create-next-app@latest my-typescript-app --typescript --eslint --tailwind --app

This command does the following:

  • Creates a new Next.js project named `my-typescript-app`.
  • Uses the `–typescript` flag to initialize the project with TypeScript support.
  • Includes ESLint for code linting to maintain code quality using the `–eslint` flag.
  • Includes Tailwind CSS for styling using the `–tailwind` flag.
  • Uses the `–app` directory for the App Router.

Navigate into your project directory:

cd my-typescript-app

Understanding the Project Structure

After the project is created, your directory structure should look similar to this:

my-typescript-app/
├── app/
│   ├── layout.tsx
│   ├── page.tsx
│   └── ...
├── public/
│   ├── ...
├── .eslintrc.json
├── next.config.js
├── package.json
├── postcss.config.js
├── tailwind.config.ts
├── tsconfig.json
└── ...

Here’s a brief overview of the key files and directories:

  • `app/`: Contains your application’s routes and components, using the new App Router.
  • `app/layout.tsx`: The root layout component, shared across all pages.
  • `app/page.tsx`: The main page component for the root route (`/`).
  • `public/`: Holds static assets like images and fonts.
  • `tsconfig.json`: Configures TypeScript compiler options.
  • `package.json`: Lists project dependencies and scripts.
  • `next.config.js`: Configures Next.js specific settings.
  • `tailwind.config.ts`: Configures Tailwind CSS.

Writing Your First TypeScript Component

Let’s modify the `app/page.tsx` file to display a simple “Hello, World!” message. Open `app/page.tsx` and replace its content with the following code:

// app/page.tsx
import Image from 'next/image'
import styles from './page.module.css'

export default function Home() {
  return (
    <main className={styles.main}>
      <div className={styles.description}>
        <p>Get started by editing <code>app/page.tsx</code></p>
        <div>
          <a
            href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
            target="_blank"
            rel="noopener noreferrer"
          >
            By <img src="/vercel.svg" alt="Vercel Logo" className={styles.vercelLogo} width={100} height={24} />
          </a>
        </div>
      </div>

      <div className={styles.center}>
        <Image
          className={styles.logo}
          src="/next.svg"
          alt="Next.js Logo"
          width={180}
          height={37}
          priority
        />
      </div>

      <div className={styles.grid}>
        <a
          href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
          className={styles.card}
          target="_blank"
          rel="noopener noreferrer"
        >
          <h2 className={styles.h2}>Docs <span>-&gt;</span></h2>
          <p className={styles.p}>Find in-depth information about Next.js features and API.</p>
        </a>

        <a
          href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
          className={styles.card}
          target="_blank"
          rel="noopener noreferrer"
        >
          <h2 className={styles.h2}>Learn <span>-&gt;</span></h2>
          <p className={styles.p}>Learn about Next.js in an interactive course with <span>quizzes!</span></p>
        </a>

        <a
          href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
          className={styles.card}
          target="_blank"
          rel="noopener noreferrer"
        >
          <h2 className={styles.h2}>Templates <span>-&gt;</span></h2>
          <p className={styles.p}>Explore the Next.js 13 template gallery.</p>
        </a>

        <a
          href="https://vercel.com/new?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
          className={styles.card}
          target="_blank"
          rel="noopener noreferrer"
        >
          <h2 className={styles.h2}>Deploy <span>-&gt;</span></h2>
          <p className={styles.p}>Instantly deploy your Next.js site to a shareable URL with Vercel.</p>
        </a>
      </div>
    </main>
  )
}

This code imports `Image` from `next/image` and styles from `./page.module.css`. It then defines a functional component called `Home` that returns JSX. The JSX includes some basic HTML elements, including a main element with a description, a center, and a grid. To run the application, use the command `npm run dev` in the terminal.

To start the development server, run:

npm run dev
# or
yarn dev
# or
pnpm dev

This will start the development server, usually on `http://localhost:3000`. Open this address in your browser, and you should see the default Next.js welcome page. You’ve successfully set up your first Next.js page with TypeScript!

Adding TypeScript Types

One of the main benefits of using TypeScript is its ability to catch type errors early. Let’s create a simple component that takes a `name` prop and displays a greeting. Create a new file called `components/Greeting.tsx` in your `app` directory:

// app/components/Greeting.tsx
interface GreetingProps {
  name: string;
}

export default function Greeting({ name }: GreetingProps) {
  return <p>Hello, {name}!</p>;
}

In this code:

  • We define an interface `GreetingProps` that specifies the type of the `name` prop as a string.
  • The `Greeting` component destructures the `name` prop and renders a greeting message.

Now, let’s use this component in `app/page.tsx`:

// app/page.tsx
import Greeting from './components/Greeting';
import Image from 'next/image'
import styles from './page.module.css'

export default function Home() {
  return (
    <main className={styles.main}>
      <div className={styles.description}>
        <p>Get started by editing <code>app/page.tsx</code></p>
        <div>
          <a
            href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
            target="_blank"
            rel="noopener noreferrer"
          >
            By <img src="/vercel.svg" alt="Vercel Logo" className={styles.vercelLogo} width={100} height={24} />
          </a>
        </div>
      </div>

      <div className={styles.center}>
        <Image
          className={styles.logo}
          src="/next.svg"
          alt="Next.js Logo"
          width={180}
          height={37}
          priority
        />
      </div>

      <div className={styles.grid}>
        <a
          href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
          className={styles.card}
          target="_blank"
          rel="noopener noreferrer"
        >
          <h2 className={styles.h2}>Docs <span>-&gt;</span></h2>
          <p className={styles.p}>Find in-depth information about Next.js features and API.</p>
        </a>

        <a
          href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
          className={styles.card}
          target="_blank"
          rel="noopener noreferrer"
        >
          <h2 className={styles.h2}>Learn <span>-&gt;</span></h2>
          <p className={styles.p}>Learn about Next.js in an interactive course with <span>quizzes!</span></p>
        </a>

        <a
          href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
          className={styles.card}
          target="_blank"
          rel="noopener noreferrer"
        >
          <h2 className={styles.h2}>Templates <span>-&gt;</span></h2>
          <p className={styles.p}>Explore the Next.js 13 template gallery.</p>
        </a>

        <a
          href="https://vercel.com/new?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
          className={styles.card}
          target="_blank"
          rel="noopener noreferrer"
        >
          <h2 className={styles.h2}>Deploy <span>-&gt;</span></h2>
          <p className={styles.p}>Instantly deploy your Next.js site to a shareable URL with Vercel.</p>
        </a>
      </div>
       <Greeting name="John Doe" />
    </main>
  )
}

Here, we import the `Greeting` component and pass the `name` prop. If you make a mistake in the prop type (e.g., passing a number instead of a string), TypeScript will immediately flag the error during development. This is a significant advantage in catching bugs early.

Working with Data: Fetching Data in Next.js with TypeScript

Data fetching is a crucial part of most web applications. Next.js provides several ways to fetch data, including:

  • Server-Side Rendering (SSR): Fetch data on the server and render the HTML.
  • Static Site Generation (SSG): Fetch data at build time and generate static HTML.
  • Client-Side Fetching: Fetch data in the browser using `fetch` or a library like `axios`.

Let’s look at an example of fetching data using SSG. First, create a new file named `app/products/page.tsx`:


// app/products/page.tsx
import { GetStaticProps } from 'next';

interface Product {
  id: number;
  title: string;
  description: string;
}

async function getProducts(): Promise<Product[]> {
  const res = await fetch('https://fakestoreapi.com/products');
  const products: Product[] = await res.json();
  return products;
}

export default async function ProductsPage() {
  const products = await getProducts();

  return (
    <div>
      <h1>Products</h1>
      <ul>
        {products.map((product) => (
          <li key={product.id}>
            <h2>{product.title}</h2>
            <p>{product.description}</p>
          </li>
        ))}
      </ul>
    </div>
  );
}

In this example:

  • We define a `Product` interface to specify the data structure.
  • The `getProducts` function fetches data from a public API (`https://fakestoreapi.com/products`).
  • The `ProductsPage` function fetches products using `getProducts` and displays them.

To view the product listing, navigate to `/products` in your browser. This example uses SSG; the data is fetched at build time. For SSR, you would use a function like `getServerSideProps`.

Styling with Tailwind CSS

The `create-next-app` command includes Tailwind CSS for styling. Tailwind CSS is a utility-first CSS framework that provides a set of pre-defined classes that you can use to style your components. To use Tailwind CSS, add the classes to your JSX elements.

For example, to style the `Greeting` component with Tailwind:


// app/components/Greeting.tsx
interface GreetingProps {
  name: string;
}

export default function Greeting({ name }: GreetingProps) {
  return <p className="text-lg font-semibold text-blue-500">Hello, {name}!</p>;
}

In this code, we added Tailwind classes to the `p` tag: `text-lg` (font size), `font-semibold` (font weight), and `text-blue-500` (text color). You can customize your styles by modifying `tailwind.config.ts` file.

Routing and Navigation

Next.js simplifies routing. With the App Router, each file in the `app` directory creates a route. For example, `app/products/page.tsx` creates the route `/products`.

To add navigation, you can use the `Link` component from `next/link`. Modify the `app/page.tsx` as follows:


// app/page.tsx
import Link from 'next/link';
import Image from 'next/image'
import styles from './page.module.css'

export default function Home() {
  return (
    <main className={styles.main}>
      <div className={styles.description}>
        <p>Get started by editing <code>app/page.tsx</code></p>
        <div>
          <a
            href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
            target="_blank"
            rel="noopener noreferrer"
          >
            By <img src="/vercel.svg" alt="Vercel Logo" className={styles.vercelLogo} width={100} height={24} />
          </a>
        </div>
      </div>

      <div className={styles.center}>
        <Image
          className={styles.logo}
          src="/next.svg"
          alt="Next.js Logo"
          width={180}
          height={37}
          priority
        />
      </div>

      <div className={styles.grid}>
        <a
          href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
          className={styles.card}
          target="_blank"
          rel="noopener noreferrer"
        >
          <h2 className={styles.h2}>Docs <span>-&gt;</span></h2>
          <p className={styles.p}>Find in-depth information about Next.js features and API.</p>
        </a>

        <a
          href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
          className={styles.card}
          target="_blank"
          rel="noopener noreferrer"
        >
          <h2 className={styles.h2}>Learn <span>-&gt;</span></h2>
          <p className={styles.p}>Learn about Next.js in an interactive course with <span>quizzes!</span></p>
        </a>

        <a
          href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
          className={styles.card}
          target="_blank"
          rel="noopener noreferrer"
        >
          <h2 className={styles.h2}>Templates <span>-&gt;</span></h2>
          <p className={styles.p}>Explore the Next.js 13 template gallery.</p>
        </a>

        <a
          href="https://vercel.com/new?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
          className={styles.card}
          target="_blank"
          rel="noopener noreferrer"
        >
          <h2 className={styles.h2}>Deploy <span>-&gt;</span></h2>
          <p className={styles.p}>Instantly deploy your Next.js site to a shareable URL with Vercel.</p>
        </a>
      </div>
       <Greeting name="John Doe" />
       <Link href="/products">Go to Products</Link>
    </main>
  )
}

This adds a link to the `/products` route. When the user clicks on this link, they will be navigated to the products page.

Common Mistakes and How to Fix Them

Here are some common mistakes beginners make when working with Next.js and TypeScript, along with solutions:

  • Incorrect TypeScript Setup: If TypeScript isn’t working as expected, double-check your `tsconfig.json` file. Ensure that the compiler options are correctly configured. Common issues include incorrect `jsx` settings or missing type definitions.
  • Type Errors: TypeScript can be strict, and type errors are common. Read the error messages carefully, and ensure your types match the data you’re working with. Use interfaces or types to define the structure of your data.
  • Incorrect Imports: Make sure you are importing components and modules correctly. Use relative paths for local components (e.g., `./components/MyComponent`) and absolute paths for modules (e.g., `import React from ‘react’`).
  • Server-Side vs. Client-Side Confusion: Understand the difference between server-side and client-side code. Code in `getServerSideProps` or `getStaticProps` runs on the server. Client-side code runs in the browser. Be mindful of this distinction when accessing APIs or using browser-specific features.
  • Not Using `next/image` Correctly: The `next/image` component is optimized for image loading and performance. Make sure you are providing the necessary `width`, `height`, and `alt` attributes. If you encounter issues, refer to the Next.js documentation for image optimization.

Advanced Concepts

Once you are comfortable with the basics, explore these advanced concepts:

  • API Routes: Create serverless functions using the `app/api` directory to handle API requests.
  • Middleware: Use middleware to intercept requests and customize the behavior of your application.
  • State Management: Implement state management solutions like Context API, Redux, or Zustand for managing application state.
  • Deployment: Deploy your Next.js application to platforms like Vercel, Netlify, or AWS.
  • Testing: Write unit and integration tests using Jest, React Testing Library, or Cypress to ensure the quality and reliability of your code.

Key Takeaways

  • Next.js provides a powerful framework for building web applications.
  • TypeScript enhances code quality and maintainability.
  • The App Router simplifies routing and navigation.
  • Data fetching is straightforward with SSR and SSG.
  • Tailwind CSS streamlines styling.

FAQ

Q: How do I handle environment variables in Next.js with TypeScript?

A: Create a `.env.local` file in your project root and define your environment variables there. Use `process.env.YOUR_VARIABLE` to access them in your code. For TypeScript, create a `next-env.d.ts` file in your project root to declare the types for your environment variables. Example:


// next-env.d.ts
/// <reference types="next" />

interface ProcessEnv {
  NEXT_PUBLIC_API_URL: string;
  // Add other environment variables here
}

Q: How do I deploy a Next.js application?

A: The easiest way to deploy is using Vercel, which is the platform created by the Next.js team. You can also deploy to Netlify, AWS, or other hosting providers. The deployment process typically involves pushing your code to a Git repository and configuring the deployment platform.

Q: How can I optimize images in Next.js?

A: Use the `next/image` component. It automatically optimizes images, serving different sizes and formats based on the user’s device. Provide the `src`, `width`, `height`, and `alt` attributes to the `Image` component. You can also configure image optimization settings in `next.config.js`.

Q: How do I use CSS Modules in Next.js?

A: CSS Modules are enabled by default. Create a CSS file with the `.module.css` extension (e.g., `styles.module.css`). Import the CSS file into your component and use the styles as properties of an object. Example:


// styles.module.css
.myClass {
  color: blue;
}

// MyComponent.tsx
import styles from './styles.module.css';

<div className={styles.myClass}>Hello</div>

Q: How do I add a favicon to my Next.js app?

A: Place your favicon file (e.g., `favicon.ico`) in the `public` directory. Next.js automatically serves static assets from the `public` directory. You can also add a link tag to the `<head>` of your layout file (`app/layout.tsx`) to specify the favicon. Example:


// app/layout.tsx
import './globals.css'
import { Inter } from 'next/font/google'

const inter = Inter({ subsets: ['latin'] })

export const metadata = {
  title: 'Create Next App',
  description: 'Generated by create next app',
  // Add this line
  link: [{ rel: 'icon', href: '/favicon.ico' }],
}

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body className={inter.className}>{children}</body>
    </html>
  )
}

These answers should help you get started and troubleshoot common problems. Remember to consult the Next.js documentation for detailed information.

As you continue your journey, remember that consistent practice and experimentation are key to mastering Next.js and TypeScript. Building small projects, contributing to open-source, and exploring the vast ecosystem of plugins and libraries will help you grow your skills. Embrace the power of static typing, leverage the performance benefits of Next.js, and create web applications that are both elegant and efficient. The combination of Next.js and TypeScript provides a solid foundation for any web developer looking to build modern, scalable web applications. Keep learning, keep building, and enjoy the process of creating!