React + TypeScript Setup

Setting up a modern React application with TypeScript provides type safety and an improved developer experience. While you can create a React + TypeScript project from scratch, using Next.js as a meta-framework simplifies the setup process and provides additional features for production applications.

Why Next.js?

Next.js is a React framework that provides structure, features, and optimizations for your React applications. As of 2024, even the React team recommends using a meta-framework like Next.js for new projects. Next.js handles many configuration details for you, allowing you to focus on building your application.

Note: This guide provides a brief overview of Next.js. For a comprehensive understanding, please refer to our dedicated Next.js course.

Creating a New Project

The simplest way to start a new React + TypeScript project with Next.js is using the create-next-app CLI tool:

Creating a new Next.js project

npx create-next-app@latest

This command creates a new Next.js project. It will prompt you with several configuration options:

What is your project names? > (The root folder name of your app)
Would you like to use TypeScript? > Yes
Would you like to use ESLint? > Yes
Would you like to use Tailwind CSS? > Yes/No (based on your preference, here we will mainly use Tailwind CSS)
Would you like your code insida a `src/` directory? > Yes
Would you like to use App Router (recommended)? > Yes
Would you like to use Torbupack for `next dev`? > Yes
Would you like to customize the import alias (`@/*` by default)? > No

Project Structure

After setup, your project will have a structure similar to this:

my-app/
├── .next/               # Next.js build output (generated after first build)
├── node_modules/        # Dependencies
├── public/              # Static assets
├── src/                 # Source code
│   └── app/             # App Router directory
│       ├── layout.tsx   # Root layout
│       ├── page.tsx     # Home page
│       └── globals.css  # Global styles
├── .eslint.config.mjs   # ESLint configuration
├── .gitignore           # Git ignore file
├── next.config.ts       # Next.js configuration
├── package-lock.json    # Project dependencies and scripts
├── package.json         # Project dependencies and scripts
├── postcss.config.mjs    # PostCSS configuration (if using Tailwind)
└── tsconfig.json        # TypeScript configuration

TypeScript Configuration

Next.js provides a default tsconfig.json with recommended settings for a React application. Here's what a typical configuration includes:

tsconfig.json

{
  "compilerOptions": {
    "target": "ES2017",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "bundler",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true,
    "plugins": [
      {
        "name": "next"
      }
    ],
    "paths": {
      "@/*": ["./src/*"]
    }
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
  "exclude": ["node_modules"]
}

Key TypeScript settings to understand:

  • strict: true: Enables strict type checking
  • jsx: "preserve": Preserves JSX for Next.js transformation
  • paths: Configures path aliases (e.g., @/components points to src/components)
  • plugins: Integrates with the Next.js TypeScript plugin for better type checking

Creating TypeScript Components

In a Next.js application, you can create strongly-typed React components:

TypeScript React Component

// src/components/Button.tsx
import { ReactNode, ButtonHTMLAttributes } from "react";

interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
  variant?: "primary" | "secondary" | "outline";
  size?: "sm" | "md" | "lg";
  children: ReactNode;
}

export function Button({
  variant = "primary",
  size = "md",
  children,
  className,
  ...props
}: ButtonProps) {
  // Implementation
  return (
    <button className={`button button-${variant} button-${size} ${className || ""}`} {...props}>
      {children}
    </button>
  );
}

Turbopack and React Compiler

Next.js introduced support for two major performance improvements: Turbopack and React Compiler.

Turbopack

Turbopack is a Rust-based successor to Webpack, focused on speed and incremental compilation. It's designed to provide much faster development and build times for Next.js applications.

To enable Turbopack in development:

Enabling Turbopack in package.json

{
    ...
    "scripts": {
        "dev": "next dev --turbopack",
        ...
    },
    ...
}

React Compiler (Formerly React Forget)

React Compiler (previously known as React Forget) is a compiler that automatically optimizes React components by inserting memo and other performance optimizations where beneficial. It analyzes your code and determines when re-renders are needed without manual optimization.

To enable React Compiler in Next.js:

Enabling React Compiler

// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    reactCompiler: true,
  },
};

module.exports = nextConfig;

You'll also need to install some additional packages to make it work:

npm install -D babel-plugin-react-compiler@beta eslint-plugin-react-compiler@beta

Development Scripts

Next.js projects come with predefined scripts in package.json:

Development Scripts

{
  "scripts": {
    "dev": "next dev --turbopack",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "type-check": "tsc --noEmit"
  }
}

Adding a type-check script helps catch TypeScript errors without building the project.

Folder Structures and File Naming Conventions

When working with React and TypeScript, several conventions exist for organizing your code. Understanding these will help you navigate existing projects and make informed decisions for your own.

App Directory Structure (Next.js 13+)

Next.js App Router uses a file-system based routing approach:

src/
└── app/
    ├── layout.tsx       # Root layout (applies to all routes)
    ├── page.tsx         # Home page (/)
    ├── about/
    │   └── page.tsx     # About page (/about)
    ├── blog/
    │   ├── page.tsx     # Blog index page (/blog)
    │   └── [slug]/
    │       └── page.tsx # Blog post page (/blog/post-1)
    └── api/
        └── route.ts     # API endpoint (/api)

Common Project Organization Patterns

There are several established approaches to organize React projects:

1. Technical Folders

Organizing by technical role or file type:

src/
├── components/          # UI components
├── hooks/               # Custom React hooks
├── utils/               # Utility functions
├── lib/                 # External library wrappers
├── types/               # TypeScript definitions
├── store/               # State management
├── providers/           # Context providers
├── services/            # API services
└── app/                 # Next.js app router

Good for: Projects where the same type of code is often modified together

2. Feature-Based Organization

Grouping code by feature or domain:

src/
├── features/
│   ├── authentication/
│   │   ├── components/
│   │   ├── hooks/
│   │   ├── types/
│   │   ├── utils/
│   │   └── auth-service.ts
│   ├── dashboard/
│   │   ├── components/
│   │   ├── hooks/
│   │   └── dashboard-service.ts
│   └── user-management/
│       ├── components/
│       ├── types/
│       └── user-service.ts
├── shared/              # Shared code across features
│   ├── components/
│   ├── hooks/
│   └── utils/
└── app/                 # Next.js app router

Good for: Larger applications where features are clearly defined

3. Atomic Design

Based on Brad Frost's methodology, organizing UI components by complexity level:

src/
├── components/
│   ├── atoms/           # Basic building blocks (Button, Input, Text)
│   ├── molecules/       # Simple groups of atoms (SearchBar, FormField)
│   ├── organisms/       # Complex UI sections (Header, ProductCard)
│   ├── templates/       # Page layouts and wireframes
│   └── pages/           # Page-specific components
├── hooks/
├── utils/
└── app/

Good for: Creating consistent design systems and component libraries

4. Component Organization Approaches

Within your components directory, you might organize files in various ways:

a. Flat Structure
src/
├── components/
│   ├── Button.tsx
│   ├── Navbar.tsx
│   ├── Footer.tsx
│   └── Card.tsx

Good for: Smaller projects with fewer components

b. Grouped by Type
src/
├── components/
│   ├── ui/              # UI primitives
│   ├── layout/          # Layout components
│   ├── forms/           # Form-related components
│   └── data-display/    # Tables, lists, etc.

Good for: Medium-sized projects with clear component categories

c. Component Folder Pattern

Each component gets its own folder with related files:

src/
├── components/
│   ├── Button/
│   │   ├── Button.tsx
│   │   ├── Button.test.tsx
│   │   ├── Button.module.css
│   │   └── index.ts
│   └── Navbar/
│       ├── Navbar.tsx
│       ├── NavbarItem.tsx
│       ├── Navbar.test.tsx
│       └── index.ts

Good for: Components with multiple related files and tests

File Naming Conventions

Different projects follow different naming conventions:

1. PascalCase

Button.tsx
UserProfile.tsx
AuthContext.tsx
  • Standard for React component files
  • Matches JSX element capitalization (e.g., <Button />)
  • Recommended by React documentation

2. camelCase

button.tsx
userProfile.tsx
authContext.tsx
  • Common for utilities, hooks, and non-component files
  • Matches JavaScript variable naming
  • Often used for hooks (e.g., useAuth.ts)

3. kebab-case

button.tsx
user-profile.tsx
auth-context.tsx
  • Popular in some frameworks (especially Vue)
  • Easier to type (no Shift key needed)
  • Matches URL and CSS class naming patterns

4. snake_case

button.tsx
user_profile.tsx
auth_context.tsx
  • Less common in React ecosystem
  • Used in some legacy or Python-influenced projects
  • Easy to read in filenames

Type Definition Patterns

For TypeScript files, you'll encounter different approaches to organizing types:

1. Inline Types

// Button.tsx
interface ButtonProps {
  variant: "primary" | "secondary";
  children: React.ReactNode;
}

export function Button({ variant, children }: ButtonProps) {
  // ...
}

2. Separate Type Files

// types/components.ts
export interface ButtonProps {
  variant: "primary" | "secondary";
  children: React.ReactNode;
}

// Button.tsx
import { ButtonProps } from "@/types/components";

export function Button({ variant, children }: ButtonProps) {
  // ...
}

3. Co-located Type Files

src/
├── components/
│   └── Button/
│       ├── Button.tsx
│       └── Button.types.ts

4. Barrel Files

Using index files to group and re-export types:

// types/index.ts
export * from "./user";
export * from "./auth";
export * from "./api";

Choosing the Right Approach

When setting up your project, consider:

  1. Team size: Larger teams benefit from more structured approaches
  2. Project complexity: More complex projects need clearer organization
  3. Growth expectations: Choose patterns that scale with your project
  4. Team familiarity: Consider what your team is already comfortable with
  5. Future maintenance: Choose patterns that make code easier to find and modify
  6. Consistency: Whatever pattern you choose, apply it consistently

Most importantly, document your chosen conventions for your team, especially if you're deviating from common practices.

Summary

Setting up React with TypeScript using Next.js provides numerous benefits:

  • Type safety across your entire application
  • Better developer experience with autocompletion and error catching
  • Improved code quality and reduced runtime errors
  • Simplified project structure with Next.js conventions
  • Built-in performance optimizations with Turbopack and React Compiler

While the initial setup requires some configuration, the long-term benefits in code quality, maintainability, and developer productivity make it worthwhile for most projects. Next.js handles much of the complex configuration, allowing you to focus on building your application with the type safety benefits of TypeScript.

Remember that this guide provides a starting point for React + TypeScript with Next.js. For more comprehensive understanding of Next.js features and patterns, refer to our dedicated Next.js course.