message
Web Application Development

How to Use React with TypeScript (Without Losing Your Mind)

Blog bannerBlog banner

“Hey, why use TypeScript with React?”
“Because chaos is only fun in movies, not in production code.”

A Quick Intro: What Are React and TypeScript?

React: The UI Library We All Love

React is a JavaScript library developed by Meta for building user interfaces. It's component-based, fast, and declarative — meaning you describe what you want, and React figures out the how. It's like giving directions with Google Maps instead of yelling from a moving car.

TypeScript: JavaScript with Superpowers

TypeScript is a superset of JavaScript that adds static typing, meaning you can catch bugs during development rather than in production. It turns your IDE into a super-powered assistant, giving you autocomplete, refactoring tools, and helpful error messages.

Together, React + TypeScript makes for safer, cleaner, and more scalable front-end code. It’s like pairing peanut butter with jelly — once you try it, you won’t want to go back.

Meet React’s New Best Friend: TypeScript

You’ve probably used React before. You’ve probably typed props as any and called it a day. And somewhere, a TypeScript fan quietly shed a tear. 

But today, we're going full TypeScript. No more any, no more mystery props. Just clean, predictable code with IDE support that makes you feel like a genius.

Let’s dive in.

Setting Up: Your First Date with TypeScript (Using Vite!)

First things first — forget CRA. We’re going with Vite because it’s faster, more modern, and has better TypeScript support out of the box. Let’s get started.

Open your terminal and type:

Code

npm create vite@latest my-ts-app --template react-ts
          

This command will set up a new React project with TypeScript, powered by Vite. It’s like going from a 1990s flip phone to the latest iPhone. And who doesn’t want that?

TypeScript Basics in React: No More “Guess the Props”

Let’s say you have a simple Namaste component:

Code

type NamasteProps = {
name: string;
age?: number;
};

const Namaste: React.FC<NamasteProps> = ({ name, age }) => (
<h1>
Namaste, {name}! {age && `(Age: ${age})`}
  </h1>
);

What just happened?

  • We created a NamasteProps type.
  • We used React.FC to type the component.
  • age is optional, so we added the ?.

“But wait, I could just use PropTypes.”
Yeah… and you could also use a flip phone in 2025, but why would you?

Bonus: Use useState with Types Like a Pro

Code

const [count, setCount] = useState<number>(0);

If you don’t provide the type, TypeScript will infer it — but if you need to be explicit (and you often should), this is the way.

For more complex state:

Code

type User = { name: string; loggedIn: boolean };
const [user, setUser] = useState<User | null>(null); 

Boom. No more guessing what's inside your state.

Typing Events in React

Ever been confused by event types? You’re not alone.

Code

const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
	console.log(e.target.value);
};

“I used to just put any here…”
“Yeah, and now we don’t talk about that phase.”

Typing Props for Components with Children

Let’s say you have a layout component:

Code

type LayoutProps = {
	children: React.ReactNode;
};

const Layout = ({ children }: LayoutProps) => <div className="layout">{children}</div>;

This makes sure that children are typed properly. No more warnings from your IDE or unhelpful runtime errors.

The Magic of useReducer with TypeScript

Here’s where TypeScript really shines — complex logic and state management.

Code

type State = { count: number };
type Action = { type: 'increment' } | { type: 'decrement' };

function reducer(state: State, action: Action): State {
switch (action.type) 
case 'increment':
		return { count: state.count + 1 };
case 'decrement':
		return { count: state.count - 1 };
 	default:
return state;
  }
}
const [state, dispatch] = useReducer(reducer, { count: 0 });

It’s like React and TypeScript are reading each other’s minds here.

Generics in React: Making Your Code Reusable & Type-Safe

Once you’ve mastered typing individual components, generics are your next power-up.

Let’s say you’re building a reusable `Select` component:

Code

type SelectProps<T> = {
options: T[];
onSelect: (value: T) => void;
};

function Select<T>({ options, onSelect }: SelectProps<T>) {
return (
<select onChange={(e) => onSelect(options[+e.target.value])}>
  {options.map((opt, idx) => (
  <option value={idx} key={idx}>
      {String(opt)}
      </option>
    ))}
  </select>
  );
}

Usage:
<Select
options={['Apple', 'Banana', 'Cherry']}
onSelect={(val) => console.log(val)}/>

Here, TypeScript knows exactly what type of value you're selecting — no casting, no `any`, just clean type inference.

Tip: Always define the generic type on both the props and the function component (`<T>`), or TypeScript will lose track.

This becomes really handy in custom hooks too.

Gotchas When Using React with TypeScript

"TypeScript will protect you from a lot, but it won’t protect you from yourself."

Even the best setups come with a few traps. Here's what to watch out for:

1.  Overusing `any`

Code

Yes, it's tempting. No, it’s not a solution.

const handleClick = (event: any) => {
// works, but why even use TypeScript at this point?
}

Code

âś… Better:

const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
// now you get autocomplete and safety
}

2.  Forgetting to Type Props (and Thinking It'll Be Fine)

Code

const Card = ({ title }) => <h2>{title}</h2>; // ❌ No prop types

This will compile, but it's a hidden trap.

Code

âś… Better:

type CardProps = {
	title: string;
};

const Card = ({ title }: CardProps) => <h2>{title}</h2>;

3. `useRef` and Null Values

React’s `useRef` can be confusing with TypeScript. Especially if you're referencing a DOM element.

Code

const inputRef = useRef<HTMLInputElement>(null);

useEffect(() => {
	inputRef.current?.focus(); // âś… Safe access using optional chaining
}, []);

If you try to access `.current` without checking for `null`, TypeScript will throw a fit — and rightly so.

4. Confusing Type Inference with Type Safety

Sometimes TypeScript infers types that are *technically correct* but *contextually wrong*.

Code

const [user, setUser] = useState(null); // inferred as "any"

Code

âś… Fix:

type User = { name: string; email: string };
const [user, setUser] = useState<User | null>(null);

Explicit types are your friend.

5. ‍Using `React.FC` Without Understanding It

While `React.FC` is handy, it also brings along implicit children and typing quirks. Use it *if* you need those features — otherwise, you can skip it.

Code

// Optional: Only if you want implicit children typing
const OurComponent: React.FC<{ name: string }> = ({ name }) => <p>{name}</p>;

// Alternative:
type Props = { name: string };
const OurComponent = ({ name }: Props) => <p>{name}</p>;

6. Component Composition Issues with Generic Types

If you're building generic components like a reusable `Select<T>` — TypeScript gets *real* picky. Make sure to explicitly define generics:

Code

type SelectProps<T> = {
  options: T[];
  onSelect: (value: T) => void;
};

function Select<T>({ options, onSelect }: SelectProps<T>) {
// ...
}

Gotchas like these are where TypeScript goes from "magic" to "mystery box" — but once you get it, it clicks.

Best Practices for Using React with TypeScript

Here’s how to keep your code clean, safe, and scalable:

  • Be explicit when it matters
    TypeScript is great at inference, but don’t rely on it blindly — especially for props, state, and function return types.

  • Avoid `any` like it’s radioactive
    If you must use it (temporarily), comment why. Otherwise, TypeScript can’t help you.

  • Prefer `type` for objects, `interface` for components (or pick one and stick to it)  Both are valid, but consistency helps maintainability.

  • Use `React.ReactNode` for children, not `any`
    Avoid lazy typing. Typing children properly gives you better tooling support.

  • Type hooks explicitly 
    `useState<Type>()`, `useRef<Type>(null)`, `useReducer<State, Action>()` — better DX, fewer bugs.

  • Name types clearly and consistently 
    Instead of `Props`, use `UserCardProps`, `SelectProps<T>`, etc. It’s easier to trace.

  • Embrace composition and reusability with generics 
    Generics unlock powerful, reusable components and hooks.

  • Let your IDE help 
    Lean into autocomplete, inline docs, and TypeScript error messages. They’re annoyingly accurate for a reason.

  • Refactor types, not just code 
    As your app grows, extract and reuse types just like you would with functions or components.

Conclusion: Embrace the (Type)Script

React and TypeScript might feel like an arranged marriage at first. But give it time, and you’ll realize they’re actually soulmates. You’ll write fewer bugs, ship faster, and your future self will thank you.

“Wow, this code just works.”
“That’s TypeScript for you.”

card user img
Twitter iconLinked icon

A problem solver with a passion for building robust, scalable web solutions that push the boundaries of technology and deliver impactful results

card user img
Twitter iconLinked icon

Driven by a passion for building interactive and responsive web applications, focused on crafting smooth user experiences with clean, maintainable React code.

Book a FREE Consultation

No strings attached, just valuable insights for your project

Valid number
Please complete the reCAPTCHA verification.
Claim My Spot!
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
download ready
Thank You
Your submission has been received.
We will be in touch and contact you soon!

Our Latest Blogs

View All Blogs