Next.js Using TypeScript

TypeScript is JavaScript with types. It adds a layer of safety to your code by letting you define exactly what kind of data a variable, function parameter, or component prop expects. Next.js has built-in TypeScript support — no extra plugins needed.

Why Use TypeScript

JavaScript lets you pass any value anywhere. TypeScript catches mismatches before your code runs. This prevents entire categories of bugs that would otherwise only appear in production.

JAVASCRIPT (no type safety):
function greet(user) {
  return 'Hello, ' + user.name;
}
greet(42);   // No error shown, but crashes at runtime: 42.name is undefined

TYPESCRIPT (type safety):
function greet(user: { name: string }) {
  return 'Hello, ' + user.name;
}
greet(42);   // Error shown immediately: Argument of type 'number' is not assignable

Setting Up TypeScript in Next.js

When you create a new project with create-next-app, select "Yes" when asked about TypeScript. The setup is complete automatically.

For an existing JavaScript project, rename your files from .js to .ts (or .tsx for files containing JSX). Then run:

npm install --save-dev typescript @types/react @types/node

Start the development server once. Next.js creates a tsconfig.json file automatically.

File Extensions

.js   → JavaScript (no types)
.ts   → TypeScript (no JSX)
.tsx  → TypeScript with JSX (use this for React components)
.jsx  → JavaScript with JSX

Typing Component Props

Define the shape of your component's props using a TypeScript type or interface. This tells TypeScript and your editor exactly what values the component expects.

// Without TypeScript:
function Button({ label, onClick, disabled }) { ... }

// With TypeScript:
type ButtonProps = {
  label: string;
  onClick: () => void;
  disabled?: boolean;   // The ? means this prop is optional
};

export default function Button({ label, onClick, disabled }: ButtonProps) {
  return (
    <button onClick={onClick} disabled={disabled}>
      {label}
    </button>
  );
}
DIAGRAM: TypeScript Catching a Prop Error

<Button label={42} />
         ↑
TypeScript error: Type 'number' is not assignable to type 'string'
Error shown in editor before code even runs

Typing Page Props (params and searchParams)

Next.js pages receive params and searchParams as props. Type them explicitly to avoid errors when accessing their values.

// app/blog/[slug]/page.tsx

type Props = {
  params: {
    slug: string;
  };
  searchParams: {
    [key: string]: string | string[] | undefined;
  };
};

export default async function BlogPost({ params, searchParams }: Props) {
  const post = await fetchPost(params.slug);

  return <article>{post.title}</article>;
}

Typing API Routes

// app/api/users/route.ts
import { NextRequest, NextResponse } from 'next/server';

type User = {
  id: number;
  name: string;
  email: string;
};

export async function GET(request: NextRequest): Promise<NextResponse<User[]>> {
  const users: User[] = await fetchUsers();
  return NextResponse.json(users);
}

Common TypeScript Patterns in Next.js

Typing useState

const [user, setUser] = useState<User | null>(null);
const [count, setCount] = useState<number>(0);
const [items, setItems] = useState<string[]>([]);

Typing Event Handlers

function handleInput(event: React.ChangeEvent<HTMLInputElement>) {
  console.log(event.target.value);
}

function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
  event.preventDefault();
}

Typing Async Data

type Post = {
  id: number;
  title: string;
  body: string;
};

async function fetchPost(slug: string): Promise<Post> {
  const res = await fetch(`/api/posts/${slug}`);
  return res.json();
}

The tsconfig.json File

Next.js generates a tsconfig.json with sensible defaults. You rarely need to edit it. The most important setting is strict: true, which turns on all of TypeScript's strictest checks. Keep this enabled — it catches more bugs.

{
  "compilerOptions": {
    "strict": true,           ← Keep this enabled
    "target": "ES2017",
    "lib": ["dom", "esnext"],
    "module": "esnext",
    "jsx": "preserve",
    "paths": {
      "@/*": ["./*"]          ← Enables @/components shortcut imports
    }
  }
}

TypeScript Does Not Slow Your App

TypeScript types exist only during development. The TypeScript compiler strips all type annotations before running the code. Your production app runs plain JavaScript — TypeScript has zero runtime cost.

DEVELOPMENT:
TypeScript code → TypeScript compiler checks types → Shows errors immediately

PRODUCTION BUILD:
TypeScript code → Types stripped out → Plain JavaScript runs
                                         ↑ No performance difference from JavaScript

Key Takeaway

TypeScript adds type safety to your Next.js code, catching bugs before they reach users. Use .tsx files for React components and .ts for non-JSX TypeScript. Define prop types with type or interface. Type your page props, API routes, and async functions. TypeScript types disappear at runtime, so there is no performance cost in production.

Leave a Comment