I’ve seen how quickly a project can spiral into a debugging nightmare without proper typing.
Adding TypeScript to your React components is one of the best moves you can make to ensure your code is predictable and easy to maintain.
In this tutorial, I will show you the different ways to type React components using TypeScript, based on what I’ve used in production environments.
We will move past simple “Hello World” examples and use scenarios you’d actually encounter, like managing a New York real estate listing or a California-based payroll system.
Method 1: Type Functional Components with React.FC
When I first started integrating TypeScript with React, React.FC (or React.FunctionComponent) was the standard approach I used.
It provides an explicit way to define a component and automatically includes the children prop, though modern versions require you to be more explicit about it.
Here is an example of a “Property Listing” component for a real estate site based in Miami.
import React from 'react';
// Defining the shape of our Property props
interface PropertyProps {
address: string;
price: number;
isAvailable: boolean;
onInquiry: (address: string) => void;
children?: React.ReactNode;
}
// Using React.FC to type the component
const PropertyCard: React.FC<PropertyProps> = ({ address, price, isAvailable, onInquiry, children }) => {
return (
<div style={{ border: '1px solid #ccc', padding: '20px', margin: '10px', borderRadius: '8px' }}>
<h2>Location: {address}</h2>
<p>Market Price: ${price.toLocaleString('en-US')}</p>
<p>Status: {isAvailable ? 'Active Listing' : 'Under Contract'}</p>
<button onClick={() => onInquiry(address)}>Schedule a Tour</button>
<div className="additional-details">
{children}
</div>
</div>
);
};
// Implementation example
const MiamiRealEstate: React.FC = () => {
const handleInquiry = (addr: string) => {
console.log(`Inquiry sent for: ${addr}`);
};
return (
<PropertyCard
address="123 Ocean Drive, Miami, FL"
price={1250000}
isAvailable={true}
onInquiry={handleInquiry}
>
<p>Note: This property includes a private beach access and a renovated kitchen.</p>
</PropertyCard>
);
};
export default MiamiRealEstate;You can refer to the screenshot below to see the output.

I find this method particularly useful when I want the IDE to immediately recognize the component as a React Functional Component, which helps with auto-completion.
Method 2: Use Standard Function Declarations
As I progressed in my career, I started leaning toward standard function declarations instead of React.FC.
This approach is often cleaner and avoids some of the subtle issues with React.FC, such as the automatic (and sometimes unwanted) inclusion of children in older versions.
Let’s look at a “Payroll Summary” component for a tech firm in Silicon Valley.
import React from 'react';
// Defining our Employee interface
interface EmployeeData {
employeeId: string;
fullName: string;
salary: number;
department: 'Engineering' | 'Marketing' | 'Sales';
}
interface PayrollProps {
employees: EmployeeData[];
taxRate: number;
}
// Using a standard function declaration with destructuring and types
function PayrollDashboard({ employees, taxRate }: PayrollProps): JSX.Element {
const calculateNetPay = (gross: number) => gross - (gross * taxRate);
return (
<div className="dashboard-container">
<h1>Silicon Valley Tech - Monthly Payroll</h1>
<table>
<thead>
<tr>
<th>Name</th>
<th>Department</th>
<th>Gross Pay</th>
<th>Estimated Net Pay (USD)</th>
</tr>
</thead>
<tbody>
{employees.map((emp) => (
<tr key={emp.employeeId}>
<td>{emp.fullName}</td>
<td>{emp.department}</td>
<td>${emp.salary.toLocaleString()}</td>
<td>${calculateNetPay(emp.salary).toLocaleString()}</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
// Example usage
const AppPayroll = () => {
const staff: EmployeeData[] = [
{ employeeId: 'SV-001', fullName: 'John Doe', salary: 150000, department: 'Engineering' },
{ employeeId: 'SV-002', fullName: 'Jane Smith', salary: 145000, department: 'Marketing' }
];
return <PayrollDashboard employees={staff} taxRate={0.33} />;
};
export default AppPayroll;You can refer to the screenshot below to see the output.

I prefer this method for most of my enterprise projects because it makes the return type (JSX.Element) explicit and behaves like a regular JavaScript function.
Method 3: Type Components with Optional and Readonly Props
In large-scale US retail applications, you often deal with data that shouldn’t be modified or optional UI elements.
I use the readonly modifier to ensure that props are not accidentally mutated within the component, and the? operator for optional fields.
Consider a “Retail Product Card” for a national chain like Walmart or Target.
import React from 'react';
interface ProductProps {
readonly sku: string; // SKU should never change
title: string;
price: number;
discountCode?: string; // Optional prop
inStock: boolean;
}
const RetailProduct: React.FC<ProductProps> = ({ sku, title, price, discountCode, inStock }) => {
return (
<div className="product-item">
<h3>{title}</h3>
<p>SKU: {sku}</p>
<p>Price: ${price.toFixed(2)}</p>
{discountCode && (
<p style={{ color: 'green' }}>Applied Discount: {discountCode}</p>
)}
<p>{inStock ? 'Available in nearby stores' : 'Out of stock'}</p>
<button disabled={!inStock}>Add to Cart</button>
</div>
);
};
// Usage
const StoreCatalog = () => {
return (
<div>
<RetailProduct
sku="US-RET-990"
title="Premium Coffee Maker"
price={89.99}
inStock={true}
/>
<RetailProduct
sku="US-RET-102"
title="Stainless Steel Kettle"
price={45.00}
discountCode="SPRING20"
inStock={false}
/>
</div>
);
};You can refer to the screenshot below to see the output.

Using readonly is a habit I picked up while working on financial dashboards, where data integrity is paramount.
Method 4: Type Event Handlers in Components
Handling events is where many developers get tripped up with TypeScript.
I’ve learned that being specific about the event type (like React.ChangeEvent or React.FormEvent) is crucial for accessing properties like target.value.
Let’s build a “Voter Registration Form” for a local US election.
import React, { useState } from 'react';
const VoterRegistration: React.FC = () => {
const [voterName, setVoterName] = useState<string>('');
const [zipCode, setZipCode] = useState<string>('');
// Typing the change event for an input field
const handleNameChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setVoterName(event.target.value);
};
const handleZipChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setZipCode(event.target.value);
};
// Typing the form submission event
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
console.log(`Registering voter: ${voterName} in ZIP ${zipCode}`);
alert('Registration submitted for processing.');
};
return (
<div style={{ maxWidth: '400px', margin: 'auto' }}>
<h2>US Voter Registration Portal</h2>
<form onSubmit={handleSubmit}>
<div>
<label>Full Name:</label>
<input type="text" value={voterName} onChange={handleNameChange} required />
</div>
<div>
<label>ZIP Code:</label>
<input type="text" value={zipCode} onChange={handleZipChange} maxLength={5} required />
</div>
<button type="submit">Submit Registration</button>
</form>
</div>
);
};
export default VoterRegistration;In my experience, using React.ChangeEvent<HTMLInputElement> is much better than using any, as it gives you full intellisense for the event object.
Method 5: Use Union Types for Component States
Often, a component can be in one of several specific states, for example, a shipping tracker for a logistics company like FedEx or UPS.
I use Union Types to define these specific allowed strings, which prevents typos and invalid states.
import React from 'react';
// Shipping status can only be one of these specific strings
type ShippingStatus = 'Processing' | 'In Transit' | 'Out for Delivery' | 'Delivered';
interface TrackerProps {
trackingNumber: string;
status: ShippingStatus;
estimatedArrival: string;
}
const ShippingTracker: React.FC<TrackerProps> = ({ trackingNumber, status, estimatedArrival }) => {
const getStatusColor = (s: ShippingStatus): string => {
switch (s) {
case 'Delivered': return 'green';
case 'Out for Delivery': return 'orange';
case 'In Transit': return 'blue';
default: return 'gray';
}
};
return (
<div className="tracker-card" style={{ borderLeft: `10px solid ${getStatusColor(status)}` }}>
<h3>Tracking #: {trackingNumber}</h3>
<p>Current Status: <strong>{status}</strong></p>
<p>Estimated Delivery: {estimatedArrival}</p>
</div>
);
};
// Usage
const LogisticsDashboard = () => {
return (
<ShippingTracker
trackingNumber="1Z999AA1012345678"
status="In Transit"
estimatedArrival="October 25, 2024"
/>
);
};This ensures that if I try to pass “Shipped” instead of “In Transit”, TypeScript will throw an error immediately during development.
Use Hooks with TypeScript
When using useState or useContext, TypeScript usually infers the type, but sometimes you need to be explicit, especially with complex objects or null values.
In this example, we manage a “US Healthcare Provider” selection.
import React, { useState } from 'react';
interface Provider {
id: number;
name: string;
specialty: string;
network: 'PPO' | 'HMO';
}
const ProviderSearch: React.FC = () => {
// Explicitly typing useState for an object that could be null initially
const [selectedProvider, setSelectedProvider] = useState<Provider | null>(null);
const selectBlueShield = () => {
setSelectedProvider({
id: 501,
name: 'Dr. Sarah Jenkins',
specialty: 'Cardiology',
network: 'PPO'
});
};
return (
<div>
<h1>Find a Healthcare Provider</h1>
<button onClick={selectBlueShield}>Select Primary Doctor</button>
{selectedProvider ? (
<div className="details">
<h3>{selectedProvider.name}</h3>
<p>Specialty: {selectedProvider.specialty}</p>
<p>Network Type: {selectedProvider.network}</p>
</div>
) : (
<p>No provider selected yet.</p>
)}
</div>
);
};Explicitly typing hooks has saved me countless hours of debugging “cannot read property of null” errors.
In this guide, I have covered the most common and effective ways to type your React components using TypeScript.
Whether you are using React.FC, standard functions, or handling complex events and states, these patterns will help you write safer code.
I recommend starting with one method and gradually incorporating the others as your application grows in complexity.
You may also like to read:
- How to Add ClassName to React Components
- React Drag and Drop Component
- Render React 18 Component to Div
- React Component Lifecycle Phases

I am Bijay Kumar, a Microsoft MVP in SharePoint. Apart from SharePoint, I started working on Python, Machine learning, and artificial intelligence for the last 5 years. During this time I got expertise in various Python libraries also like Tkinter, Pandas, NumPy, Turtle, Django, Matplotlib, Tensorflow, Scipy, Scikit-Learn, etc… for various clients in the United States, Canada, the United Kingdom, Australia, New Zealand, etc. Check out my profile.