React Component Props with TypeScript

As a React developer, I have seen many developers struggle with passing data between components.

When I first started, we used PropTypes, which worked, but it often felt like we were catching errors too late in the process.

Transitioning to TypeScript changed everything for me, especially when it comes to defining component parameters (or “props”).

Using TypeScript with React ensures that your components are predictable, easier to debug, and much more scalable.

In this tutorial, I will show you exactly how to handle React component parameters using TypeScript, using real-world examples you would actually see in a professional project.

Use TypeScript for React Component Parameters

Before we jump into the code, let’s talk about why this matters from a developer’s perspective.

When you define parameters with TypeScript, your editor provides instant feedback and autocomplete.

If you try to pass a string where a number is expected, you’ll see a red underline immediately, not a crash in the browser.

Method 1: Use Interfaces for Functional Components

This is my go-to method for most professional React projects. It keeps the component signature clean.

Interfaces allow you to define the shape of your props object clearly before you even write the component logic.

Let’s look at an example of a “Salary Calculator” component, something very common in US-based fintech applications.

import React from 'react';

// Defining the interface for our component parameters
interface SalaryDetailsProps {
  employeeName: string;
  annualSalary: number;
  state: 'NY' | 'CA' | 'TX' | 'FL'; // Restricted to specific US states
  isExempt: boolean;
}

const SalaryDetails: React.FC<SalaryDetailsProps> = ({ 
  employeeName, 
  annualSalary, 
  state, 
  isExempt 
}) => {
  const monthlyPay = (annualSalary / 12).toLocaleString('en-US', {
    style: 'currency',
    currency: 'USD',
  });

  return (
    <div style={{ padding: '20px', border: '1px solid #ccc', borderRadius: '8px' }}>
      <h2>Payroll Entry: {employeeName}</h2>
      <p>Base Monthly Pay: {monthlyPay}</p>
      <p>Tax Jurisdiction: {state}</p>
      <p>Employment Status: {isExempt ? 'Exempt (Salary)' : 'Non-Exempt (Hourly)'}</p>
    </div>
  );
};

// Usage Example
export default function PayrollApp() {
  return (
    <SalaryDetails 
      employeeName="John Doe" 
      annualSalary={125000} 
      state="NY" 
      isExempt={true} 
    />
  );
}

You can refer to the screenshot below to see the output.

Component React Props with TypeScript

In this example, I used an interface to define exactly what the component needs. If you forget to pass the state prop, TypeScript will throw an error immediately.

Method 2: Handle Optional Parameters

In many real-world scenarios, not every piece of data is required. For example, a US shipping address might include an optional “Apartment/Suite” line.

In TypeScript, we use the ? symbol to mark a parameter as optional.

import React from 'react';

interface ShippingLabelProps {
  recipientName: string;
  streetAddress: string;
  secondaryAddress?: string; // This is optional
  city: string;
  zipCode: string;
}

const ShippingLabel: React.FC<ShippingLabelProps> = ({
  recipientName,
  streetAddress,
  secondaryAddress,
  city,
  zipCode
}) => {
  return (
    <div style={{ backgroundColor: '#f9f9f9', padding: '15px', width: '300px' }}>
      <h3>Ship To:</h3>
      <p><strong>{recipientName}</strong></p>
      <p>{streetAddress}</p>
      {secondaryAddress && <p>{secondaryAddress}</p>}
      <p>{city}, {zipCode}</p>
      <p>United States</p>
    </div>
  );
};

// Usage with and without optional props
export const ShippingDashboard = () => {
  return (
    <div>
      {/* Example with optional secondary address */}
      <ShippingLabel 
        recipientName="Alice Smith"
        streetAddress="123 Wall Street"
        secondaryAddress="Suite 500"
        city="New York"
        zipCode="10005"
      />

      {/* Example without optional secondary address */}
      <ShippingLabel 
        recipientName="Bob Jones"
        streetAddress="456 Maple Avenue"
        city="Austin"
        zipCode="73301"
      />
    </div>
  );
};

I find that using optional props makes components much more reusable across different parts of an application.

Method 3: Pass Functions as Parameters

In React, we often need to pass functions (like event handlers) from a parent to a child.

This is a core concept for “Lifting State Up.” In TypeScript, we must define the function’s signature.

Let’s build a simple “Tax Credit Toggle” used in US tax preparation software.

import React, { useState } from 'react';

interface TaxToggleProps {
  label: string;
  onToggle: (isActive: boolean) => void; // A function that takes a boolean and returns nothing
}

const TaxToggle: React.FC<TaxToggleProps> = ({ label, onToggle }) => {
  return (
    <div style={{ margin: '10px 0' }}>
      <span>{label}</span>
      <button onClick={() => onToggle(true)}>Enable</button>
      <button onClick={() => onToggle(false)}>Disable</button>
    </div>
  );
};

export const TaxForm = () => {
  const [hasChildTaxCredit, setHasChildTaxCredit] = useState(false);

  const handleToggle = (status: boolean) => {
    setHasChildTaxCredit(status);
    console.log(`Tax Credit Status Changed: ${status}`);
  };

  return (
    <div>
      <h1>IRS Form 1040 Assistant</h1>
      <TaxToggle 
        label="Child Tax Credit (CTC)" 
        onToggle={handleToggle} 
      />
      <p>Status: {hasChildTaxCredit ? 'Applied' : 'Not Applied'}</p>
    </div>
  );
};

Defining the function type in the interface prevents you from accidentally passing the wrong function or calling it with the wrong arguments.

Method 4: Use Union Types for Specific Values

Sometimes you want to limit a parameter to a set of specific strings.

This is incredibly useful for US-based apps dealing with time zones or credit card types.

import React from 'react';

interface PaymentCardProps {
  cardHolder: string;
  cardType: 'Visa' | 'MasterCard' | 'Amex' | 'Discover'; // Union Type
  lastFour: string;
}

const PaymentCard: React.FC<PaymentCardProps> = ({ cardHolder, cardType, lastFour }) => {
  return (
    <div style={{ border: '1px solid black', padding: '10px', width: '250px' }}>
      <p>{cardType} ending in ****{lastFour}</p>
      <small>{cardHolder}</small>
    </div>
  );
};

export const CheckoutPage = () => {
  return (
    <PaymentCard 
      cardHolder="Michael Scott"
      cardType="Amex" // TypeScript ensures we can't type "ApplePay" here
      lastFour="9876"
    />
  );
};

I use Union types daily to ensure that only valid business logic values are passed through my components.

Method 5: Children Props and ReactNode

Sometimes your component acts as a wrapper or a container. In these cases, you need to pass React elements as parameters.

The children prop is a special parameter in React. In TypeScript, we usually type it as React.ReactNode.

import React, { ReactNode } from 'react';

interface ModalLayoutProps {
  title: string;
  children: ReactNode; // Allows any valid React element
}

const ModalLayout: React.FC<ModalLayoutProps> = ({ title, children }) => {
  return (
    <div className="modal-overlay" style={{ background: '#eee', padding: '20px' }}>
      <div className="modal-content" style={{ background: '#fff', padding: '20px' }}>
        <h1>{title}</h1>
        <hr />
        <div className="body-content">
          {children}
        </div>
      </div>
    </div>
  );
};

export const Dashboard = () => {
  return (
    <ModalLayout title="US Market Summary">
      <p>The S&P 500 is up by 1.2% today.</p>
      <button>View Full Report</button>
    </ModalLayout>
  );
};

Using ReactNode is the most flexible way to pass nested components and HTML elements.

Common Mistakes to Avoid

In my experience, even seasoned developers make a few common mistakes when typing props.

First, avoid using the any type at all costs. It defeats the entire purpose of using TypeScript.

Second, don’t over-complicate your interfaces. If an interface gets too large, it might be a sign that your component is doing too much.

Lastly, remember to export your interfaces if you plan to use them in unit tests or other components.

Defining parameters correctly is the backbone of a maintainable React codebase.

It makes the developer experience much smoother and prevents common runtime errors that can frustrate users.

I hope you found this tutorial helpful! Using TypeScript with React is one of the best decisions you can make for your professional growth.

You may also like to read:

51 Python Programs

51 PYTHON PROGRAMS PDF FREE

Download a FREE PDF (112 Pages) Containing 51 Useful Python Programs.

pyython developer roadmap

Aspiring to be a Python developer?

Download a FREE PDF on how to become a Python developer.

Let’s be friends

Be the first to know about sales and special discounts.