React Props Typing: When to Use Inline Types, Interface, or Type Declarations
I started writing React components with TypeScript and immediately hit a confusing question: how should I type my props? I saw three different approaches in tutorials and Stack Overflow answers—inline object types, interfaces, and type aliases. Each worked, but which one was “correct”?
The inline syntax looked simplest. But then I saw components with interfaces. Then type aliases. Then people arguing about which was better. I spent way too much time wondering if my choice would cause problems later.
The Three Approaches
Here’s what I found. All three work. The choice isn’t about correctness—it’s about readability and maintainability as your components grow.
Inline Object Types
function MyButton({ title }: { title: string }) { return <button>{title}</button>;}The React documentation calls this “the simplest way to provide types for a component.” It works great for components with one or two props. No extra declarations. The type lives right next to the component.
But I tried this approach with a component that had five props, and it became unreadable:
// This gets messy fastfunction MyButton({ title, disabled, onClick, className, id,}: { title: string; disabled: boolean; onClick: () => void; className: string; id: string;}) { return ( <button disabled={disabled} onClick={onClick} className={className} id={id}> {title} </button> );}The function signature stretched across multiple lines. The props destructuring and type definition competed for attention. When I needed to add another prop, I had to edit this bloated inline type.
Interface Declaration
interface MyButtonProps { title: string; disabled: boolean; onClick: () => void; className?: string; id?: string;}
function MyButton({ title, disabled, onClick, className, id }: MyButtonProps) { return ( <button disabled={disabled} onClick={onClick} className={className} id={id}> {title} </button> );}The interface separates the type from the component. The function signature stays clean. Adding props means editing the interface, not hunting through a long inline definition.
Interfaces also support JSDoc comments, which show up in editor hover hints:
interface MyButtonProps { /** The text to display inside the button */ title: string; /** Whether the button can be interacted with */ disabled: boolean; /** Handler called when button is clicked */ onClick: () => void; /** Optional CSS class for styling */ className?: string; /** Optional HTML id attribute */ id?: string;}When I hover over a prop in VS Code, I see the documentation. This helps teammates understand what each prop does without reading the component code.
Type Alias
type MyButtonProps = { title: string; disabled: boolean; onClick: () => void; className?: string; id?: string;};
function MyButton({ title, disabled, onClick, className, id }: MyButtonProps) { return ( <button disabled={disabled} onClick={onClick} className={className} id={id}> {title} </button> );}Type aliases look almost identical to interfaces for object types. The difference? Type aliases support union types and mapped types, while interfaces focus on object shapes.
For simple props, both work equally well. TypeScript treats them the same way.
When Each Approach Fits
I made a table to help decide:
| Approach | Recommended For ||-----------------|----------------------------------------------|| Inline types | 1-2 props, quick prototypes, one-off types || Interface | Multiple props, JSDoc needed, reusable types || Type alias | Union types, mapped types, project convention|The key insight: inline types become “unwieldy” (the React docs’ word) when you have multiple fields. Named types—either interface or type—solve this.
Union Types with Type Aliases
One place where type aliases shine: props that accept multiple types.
type Status = "idle" | "loading" | "success" | "error";
interface StatusBadgeProps { status: Status;}
function StatusBadge({ status }: StatusBadgeProps) { const colors = { idle: "gray", loading: "blue", success: "green", error: "red", }; return <span className={`badge badge-${colors[status]}`}>{status}</span>;}Interfaces can’t define union types directly. I need a type alias for Status, then I can use it in an interface. This pattern—type alias for the union, interface for the props object—combines both approaches effectively.
A Common Mistake: Using Primitives as Props Type
I made this mistake early on. I tried to pass a single string as the props type:
// This doesn't workfunction MyButton(title: string) { return <button>{title}</button>;}React expects props to be an object. The component receives a single props object, not individual arguments. This is wrong:
// Props must be an object type, not a primitivefunction MyButton(props: string) { return <button>{props}</button>;}The correct pattern always uses an object type, even for one prop:
// Correct: object type with one propertyfunction MyButton({ title }: { title: string }) { return <button>{title}</button>;}Interface vs Type: The Real Difference
I kept seeing debates about interface versus type. Here’s what matters for React props:
| Feature | Interface | Type Alias ||----------------------|--------------------|--------------------|| Object types | Yes | Yes || JSDoc comments | Yes | Yes || Extending | extends keyword | intersection (&) || Union types | No | Yes || Mapped types | No | Yes || Declaration merging | Yes (can augment) | No |For props, both work identically. Declaration merging matters more for library authors who want users to augment types. Union types matter when props accept multiple values.
The React documentation explicitly states: “TypeScript accepts either type or interface for describing component props.” Pick based on your team’s style convention, not technical requirements.
What I Use Now
I settled on a simple rule:
- One or two props, inline type
- Three or more props, interface
- Union types or mapped types, type alias
This keeps inline types where they’re readable and moves to named types when complexity increases. JSDoc comments go in interfaces for multi-prop components.
// Quick component, inline works finefunction PageTitle({ title }: { title: string }) { return <h1>{title}</h1>;}
// Complex component, interface for clarityinterface DataTableProps { data: Array<{ id: string; name: string; value: number }>; columns: Array<{ key: string; label: string }>; onRowClick?: (row: DataRow) => void; sortable?: boolean; filterable?: boolean;}
function DataTable({ data, columns, onRowClick, sortable, filterable }: DataTableProps) { // Component implementation}The DataTable interface documents each prop’s purpose. Team members can read the interface without hunting through component code.
Key Takeaways
- Inline types work for simple components—one or two props, no need to reuse
- Named types scale better—interfaces and type aliases keep function signatures clean
- JSDoc comments help teams—interfaces with documentation show in editor hovers
- Interface and type are interchangeable for object props—choose by convention, not correctness
- Props must be object types—never pass primitives as the props type
The debate between interface and type is mostly noise. For React props, both work. Focus on when inline becomes unwieldy, not which keyword is “right.”
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments