Skip to main content

03. JSX and React Components in TypeScript

Level: Beginner to Intermediate

ℹ️ What You'll Learn
  • Typing component props with interfaces
  • React.FC vs function declaration
  • Children prop typing
  • Event handler types
  • Creating reusable SMS StudentCard typed component
  • Avoiding any type in components

The Problem

In React + TypeScript, every component needs typed props. What if you pass the wrong data type? TypeScript catches it. What if a parent component passes a StudentCard a number instead of a Student object? TypeScript errors before runtime.

TypeScript makes React components safer by catching prop errors at write time.

Typed Component Props

interface StudentCardProps {
student: {
id: number;
name: string;
className: string;
};
}

function StudentCard({ student }: StudentCardProps) {
return (
<div>
<h3>{student.name}</h3>
<p>Class: {student.className}</p>
</div>
);
}

// ✓ Correct
<StudentCard student={{ id: 1, name: "Ravi", className: "10" }} />

// ❌ Error: missing className
// <StudentCard student={{ id: 1, name: "Ravi" }} />

Extracting Props Type

interface Student {
id: number;
name: string;
className: string;
marks: number;
}

interface StudentCardProps {
student: Student;
}

function StudentCard({ student }: StudentCardProps) {
return <div>{student.name}</div>;
}

Props with Functions

interface StudentCardProps {
student: Student;
onDelete: (id: number) => void;
onEdit: (id: number) => Promise<void>;
}

function StudentCard({ student, onDelete, onEdit }: StudentCardProps) {
return (
<div>
<h3>{student.name}</h3>
<button onClick={() => onDelete(student.id)}>Delete</button>
<button onClick={() => onEdit(student.id)}>Edit</button>
</div>
);
}

Optional Props

interface StudentCardProps {
student: Student;
onDelete?: (id: number) => void;
showMarks?: boolean;
size?: 'small' | 'medium' | 'large';
}

function StudentCard({ student, onDelete, showMarks = false, size = 'medium' }: StudentCardProps) {
return (
<div className={`card-${size}`}>
<h3>{student.name}</h3>
{showMarks && <p>Marks: {student.marks}</p>}
{onDelete && <button onClick={() => onDelete(student.id)}>Delete</button>}
</div>
);
}

Children Prop

interface CardLayoutProps {
title: string;
children: React.ReactNode;
}

function CardLayout({ title, children }: CardLayoutProps) {
return (
<div className="card">
<h2>{title}</h2>
{children}
</div>
);
}

// Usage
<CardLayout title="Student Info">
<p>Name: Ravi</p>
<p>Class: 10</p>
</CardLayout>

React.FC Type (Older Style)

// Older way (still valid)
const StudentCard: React.FC<StudentCardProps> = ({ student }) => {
return <div>{student.name}</div>;
};

// Newer way (preferred)
function StudentCard({ student }: StudentCardProps) {
return <div>{student.name}</div>;
}

Generic Components

interface ListProps<T> {
items: T[];
renderItem: (item: T) => React.ReactNode;
}

function List<T>({ items, renderItem }: ListProps<T>) {
return (
<ul>
{items.map((item, index) => (
<li key={index}>{renderItem(item)}</li>
))}
</ul>
);
}

// Usage
<List<Student>
items={students}
renderItem={(student) => student.name}
/>

Event Types

interface StudentSearchProps {
onSearch: (query: string) => void;
}

function StudentSearch({ onSearch }: StudentSearchProps) {
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
onSearch(e.target.value);
};

const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
// ...
};

const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
console.log(e.currentTarget.innerText);
};

return (
<form onSubmit={handleSubmit}>
<input onChange={handleChange} />
<button onClick={handleClick}>Search</button>
</form>
);
}

Typed Children with Specific Types

interface StudentListProps {
children: React.ReactElement<StudentItemProps>[];
}

interface StudentItemProps {
student: Student;
}

function StudentList({ children }: StudentListProps) {
return <ul>{children}</ul>;
}

function StudentItem({ student }: StudentItemProps) {
return <li>{student.name}</li>;
}

// Usage
<StudentList>
<StudentItem student={student1} />
<StudentItem student={student2} />
</StudentList>

Extending Component Props

interface BaseCardProps {
className?: string;
onClick?: () => void;
}

interface StudentCardProps extends BaseCardProps {
student: Student;
}

function StudentCard({ student, className, onClick }: StudentCardProps) {
return (
<div className={className} onClick={onClick}>
{student.name}
</div>
);
}

Default Props in TypeScript

interface StudentBadgeProps {
status: StudentStatus;
size?: 'small' | 'medium' | 'large';
}

function StudentBadge({ status, size = 'medium' }: StudentBadgeProps) {
return <span className={`badge-${size}`}>{status}</span>;
}

Component as Prop

interface FormProps {
onSubmit: (data: any) => void;
FormComponent: React.ComponentType<{ onSubmit: (data: any) => void }>;
}

function Form({ onSubmit, FormComponent }: FormProps) {
return <FormComponent onSubmit={onSubmit} />;
}

Key Takeaways

  • Type all props with interfaces
  • Use React.ReactNode for children
  • Event types prevent mistakes
  • Optional props with ?
  • Generic components for reusability
  • Next: Props in TypeScript
⚠️ Typing Mistakes
  1. Missing prop types — Component harder to use
  2. any props — Defeats TypeScript
  3. Overly complex types — Keep simple
  4. Forgetting optional mark ? — Props become required
💡 Prop Interface Naming

Convention: ComponentNameProps

interface StudentCardProps { ... }
interface TeacherFormProps { ... }
interface ExamListProps { ... }
🤖Use AI to Learn Faster

Use ChatGPT, Claude, or Copilot to go deeper on Typed Components. Try these prompts:

  • "How do you type a component that accepts children?"
  • "What's the difference between React.ReactNode and React.ReactElement?"
  • "How do you type event handlers?"
  • "Quiz me on TypeScript React components"

💡 Tip: After reading this article, paste your own code into AI and ask "What could go wrong here and why?" — fastest way to find edge cases and deepen understanding.

Quick Definitions

  • JSX and React Components in TypeScript - The main React + TypeScript concept explained in this lesson.
  • Type/interface - A contract that describes the shape of data.
  • Typed props/state - React data with clear compile-time expectations.
  • API DTO - The request or response shape shared with ASP.NET Core Web API.

Common Mistakes

  • Using any too quickly instead of defining a useful type
  • Typing props loosely and losing the benefit of TypeScript
  • Forgetting that API data can still be missing or invalid at runtime
  • Making types too complex before the component is stable
  • Not sharing clear DTO shapes between frontend and backend teams

Practice Task

Create a small React TypeScript example using JSX and React Components in TypeScript. Keep it connected to a School Management System scenario.

Suggested practice:

  1. Define a clear Student, Teacher, or Attendance type.
  2. Build a small typed component or hook.
  3. Add one valid example and one intentionally wrong example to see TypeScript errors.
  4. Explain the type contract in your own words.
  5. Rebuild the same example once without looking at the article.

Quick Revision

QuestionAnswer
What is the main idea?Use TypeScript to make JSX and React Components in TypeScript safer in React.
Where is it used?Props, state, forms, API responses, context, hooks, and routes.
What should beginners avoid?Overusing any and ignoring runtime API validation.
What is the best debugging habit?Read the TypeScript error, check the data shape, and fix the type contract.
🎯 How would you explain JSX and React Components in TypeScript in an interview?

JSX and React Components in TypeScript is a React + TypeScript concept that improves safety and maintainability. I would explain which values need types, how TypeScript catches mistakes early, and how it helps when consuming ASP.NET Core Web API responses.

🎯 Where is this used in a real React TypeScript project?

It is used in typed props, API response models, form state, route parameters, context values, custom hooks, and reusable UI components.

Next Article

Props in TypeScript ->

nexcoding.in