Back to Blog

React.FC vs Standard Functions: Which Should You Use in 2025?

Adeel Imran
Adeel Imran

If you've been working with React and TypeScript for a while, you've probably stumbled into the age-old debate: should you use React.FC or just write standard function components?

As someone who's been building React applications for over 8 years, I've seen this discussion evolve from a heated controversy to a more nuanced choice. The good news? The most problematic issues with React.FC were fixed in React 18 and TypeScript 5.1. The even better news? There's now a clear winner for most use cases.

Let me walk you through both approaches and show you why the development community has largely settled on standard function components.

The Two Approaches: A Quick Comparison

Before diving deep, let's see both patterns in action:

Standard Function Component (The Winner)

interface ButtonProps {
  title: string;
  disabled?: boolean;
  onClick: () => void;
}

// Clean, simple, and TypeScript-friendly
function Button({ title, disabled = false, onClick }: ButtonProps) {
  return (
    <button disabled={disabled} onClick={onClick}>
      {title}
    </button>
  );
}

React.FC Approach

interface ButtonProps {
  title: string;
  disabled?: boolean;
  onClick: () => void;
}

// More verbose, with explicit typing
const Button: React.FC<ButtonProps> = ({
  title,
  disabled = false,
  onClick,
}) => {
  return (
    <button disabled={disabled} onClick={onClick}>
      {title}
    </button>
  );
};

Both work, but there's more to the story.

Why React.FC Fell Out of Favor

Back in the early days of TypeScript + React, React.FC seemed like the obvious choice. It was the "official" way to type components, right? Well, not quite.

The Children Problem (Now Fixed!)

The biggest issue was that React.FC automatically added a children prop to every component, whether you wanted it or not:

// Before React 18 - this was BROKEN
const Button: React.FC<{ title: string }> = ({ title }) => {
  return <button>{title}</button>;
};

// This would compile without errors (but shouldn't!)
<Button title="Click me">
  <span>This shouldn't be allowed!</span>
</Button>;

This implicit children prop broke type safety and led to confusing bugs. Thankfully, React 18 fixed this – React.FC no longer includes children automatically.

Generic Components: Still a Problem

Here's where things get interesting. If you're building reusable components with generics, React.FC still struggles:

// This works beautifully with standard functions
interface ListProps<T> {
  items: T[];
  renderItem: (item: T) => React.ReactNode;
}

function GenericList<T extends { id: string }>({
  items,
  renderItem,
}: ListProps<T>) {
  return (
    <ul>
      {items.map((item) => (
        <li key={item.id}>{renderItem(item)}</li>
      ))}
    </ul>
  );
}

// Usage - TypeScript knows exactly what T is!
<GenericList
  items={[
    { id: "1", name: "John" },
    { id: "2", name: "Jane" },
  ]}
  renderItem={(user) => <span>{user.name}</span>} // user is properly typed!
/>;

Try doing that with React.FC – you'll run into type inference issues that'll make you want to throw your laptop out the window.

Modern Best Practices with Standard Functions

Let me show you the patterns I use in production applications:

Basic Component with Default Props

interface CardProps {
  title: string;
  description?: string;
  variant?: "primary" | "secondary";
}

function Card({ title, description, variant = "primary" }: CardProps) {
  return (
    <div className={`card card--${variant}`}>
      <h3>{title}</h3>
      {description && <p>{description}</p>}
    </div>
  );
}

Component That Accepts Children

When you need children, be explicit about it:

import { PropsWithChildren } from "react";

interface ContainerProps {
  maxWidth?: "sm" | "md" | "lg";
  className?: string;
}

function Container({
  maxWidth = "md",
  className,
  children,
}: PropsWithChildren<ContainerProps>) {
  return (
    <div className={`container container--${maxWidth} ${className || ""}`}>
      {children}
    </div>
  );
}

Advanced: Generic Component with Constraints

This is where standard functions really shine:

interface SelectOption {
  value: string;
  label: string;
}

interface SelectProps<T extends SelectOption> {
  options: T[];
  value?: T["value"];
  onChange: (option: T) => void;
  placeholder?: string;
}

function Select<T extends SelectOption>({
  options,
  value,
  onChange,
  placeholder = "Choose an option...",
}: SelectProps<T>) {
  return (
    <select
      value={value}
      onChange={(e) => {
        const option = options.find((opt) => opt.value === e.target.value);
        if (option) onChange(option);
      }}
    >
      <option value="">{placeholder}</option>
      {options.map((option) => (
        <option key={option.value} value={option.value}>
          {option.label}
        </option>
      ))}
    </select>
  );
}

The Verdict: Standard Functions Win

While React.FC is no longer broken, standard function components remain the better choice for several reasons:

  1. Simpler syntax - Less boilerplate, more readable
  2. Better generic support - Essential for reusable components
  3. Explicit contracts - You decide exactly what props to accept
  4. Modern JavaScript patterns - Works beautifully with ES6 destructuring
  5. Community consensus - Most style guides and teams prefer this approach

Quick Reference: The Complete Comparison

Feature Standard Function React.FC Winner
Syntax Simplicity ✅ Clean & minimal ❌ More verbose Standard
Generic Support ✅ Full TypeScript support ❌ Type inference issues Standard
Children Handling ✅ Explicit with PropsWithChildren ✅ Fixed in React 18 Tie
Default Props ✅ ES6 destructuring ✅ Works fine Tie
Return Type ✅ Inferred automatically ✅ Explicitly typed Tie
Bundle Size ✅ No extra overhead ✅ Negligible difference Tie
Learning Curve ✅ Standard TypeScript ❌ Framework-specific Standard
Industry Adoption ✅ Widely preferred ❌ Falling out of favor Standard

My Recommendation

Stick with standard function components. They're simpler, more flexible, and align with modern React development patterns. The only time I might consider React.FC is when working with a legacy codebase that already uses it consistently.

Here's my go-to template for new components:

interface ComponentProps {
  // Define your props here
}

function Component({ prop1, prop2 }: ComponentProps) {
  // Your component logic
  return (
    // Your JSX
  );
}

export default Component;

Clean, simple, and it just works.

What's Your Take?

The React TypeScript ecosystem keeps evolving, and while this debate has largely settled, I'm curious about your experience. Are you team React.FC or team standard functions? Have you run into any edge cases that influenced your choice?

Feel free to reach out on X or check out more of my React insights on GitHub. I'm always up for a good technical discussion!

What patterns are you using in your React projects? Let's keep the conversation going!