React: From Components to Production · Components and JSX

TypeScript in React for JavaScript Developers

Read and write the course's TSX examples if you know JavaScript: what the types add, how to type props, state, events and refs, and how to adopt TypeScript gradually.

This course writes examples in TypeScript (.tsx files) because that is how most new React projects start and what React's own documentation recommends for larger apps. If you know JavaScript, you already know almost everything in a TSX file: TSX is JSX plus type annotations. Delete the types and you have valid JavaScript. In the first module every example has a JavaScript tab so you can compare the two; after that, read the types as documentation of what a component expects.

import { useRef, useState, type ChangeEvent } from "react";

type SearchBoxProps = {
  placeholder?: string;          // optional prop
  onSearch: (query: string) => void;
};

export function SearchBox({ placeholder = "Search", onSearch }: SearchBoxProps) {
  const [query, setQuery] = useState("");                  // inferred as string
  const [results, setResults] = useState<string[]>([]);    // empty array needs a type
  const inputRef = useRef<HTMLInputElement>(null);          // DOM ref

  function handleChange(e: ChangeEvent<HTMLInputElement>) {
    setQuery(e.target.value);
  }

  return (
    <form onSubmit={(e) => { e.preventDefault(); onSearch(query); }}>
      <input ref={inputRef} value={query} onChange={handleChange} placeholder={placeholder} />
      <button type="submit">Go</button>
      <p>{results.length} results</p>
    </form>
  );
}

What the types add

  • Props types (type SearchBoxProps = {...}) describe what a component accepts. The editor autocompletes them and flags a missing or misspelled prop before you run the app.
  • State is usually inferred from the initial value. Add a type when the initial value doesn't say enough: useState<string[]>([]) or useState<User | null>(null).
  • Events have types such as ChangeEvent<HTMLInputElement> or FormEvent<HTMLFormElement>; inline handlers (onChange={(e) => ...}) infer them for you.
  • Refs to DOM nodes are useRef<HTMLInputElement>(null).
  • ! (as in document.getElementById("root")!) tells TypeScript "this is not null". as casts. Use both sparingly.

What you can ignore at first

Generic components, utility types (Partial, Omit, ComponentProps<"button">) and strict-mode compiler settings can wait. You can follow every lesson by treating the types as comments that the editor checks.

Choosing JavaScript or TypeScript

  • New project: npm create vite@latest my-app -- --template react-ts (TypeScript) or --template react (JavaScript). Frameworks such as Next.js ask during setup.
  • Existing JavaScript app: adopt gradually. Add a tsconfig.json with allowJs, rename files to .tsx one at a time, and optionally type-check plain JS with checkJs and JSDoc comments. Nothing forces an all-at-once rewrite.
  • Plain JavaScript is fine for prototypes, small widgets and teams not ready to adopt TypeScript. TypeScript pays off as the codebase and team grow: safer refactors, self-documenting props and fewer runtime surprises.

Common pitfalls

  • Fighting the compiler with any everywhere: you lose the benefits and keep the cost. Prefer unknown plus a check, or a proper type.
  • Thinking types are checked at runtime: they are erased at build time. Data from APIs still needs validation (for example with Zod).
  • Typing everything explicitly: let inference work for state, return values and inline handlers.

Key points

  • TSX = JSX + types; remove the types and it is JavaScript
  • Type props; let state and handlers infer where possible
  • Adopt TypeScript gradually with allowJs / checkJs
  • Types disappear at runtime; validate external data

Further reading: react.dev: Using TypeScript, TypeScript handbook: Migrating from JavaScript

Practice questions

Is a .tsx file valid JavaScript if you remove the type annotations?

Answer

Yes. TSX is JSX plus type annotations. TypeScript erases types at build time, so a component without its annotations is ordinary JSX that runs the same way.

When do you need to give useState an explicit type?

Answer

When the initial value doesn't describe all the values the state will hold, for example an empty array (useState<string[]>([])) or a value that starts as null (useState<User | null>(null)). Otherwise TypeScript infers the type from the initial value.

How would you move an existing JavaScript React app to TypeScript without a big rewrite?

Answer

Add a tsconfig.json with allowJs so .js/.jsx and .ts/.tsx files compile together, convert files one at a time starting with leaf components and shared utilities, optionally use checkJs with JSDoc types for files not yet converted, and tighten compiler options (strict) as coverage grows.

Do TypeScript types protect you from bad data returned by an API?

Answer

No. Types are erased at runtime, so a response shaped differently from your type is not detected. Validate external data at the boundary (for example with Zod) and derive the TypeScript type from the schema.

Open in the app to mark lessons complete, take notes and track your progress.