React: Function Components and Props

Last updated: 2026-08-26

Function components are like Lego bricks—each brick has its own shape (props) and can be assembled and combined in various ways. Props are the "interfaces" of the bricks: whatever color or size you give the brick, that’s how it will look.


1. What You'll Learn



2. A True Story of a Front-End Team

Data Flow Between Components and Props

100%
flowchart TD
    A[Parent Component] -->|"name='Alice'"| B[UserCard]
    A -->|"items=[...]"| C[ProductList]
    C -->|"item={...}"| D[ProductCard]
    B -->|"children"| E[<Avatar />]
    A -->|"onFollow callback"| B
    
    style A fill:#e3f2fd,stroke:#1565c0
    style B fill:#e8f5e9,stroke:#2e7d32
    style C fill:#fff3e0,stroke:#e65100
    style D fill:#fce4ec,stroke:#c62828

(1) Pain Point: A button you have to copy and paste 50 times

Charlie is the technical lead of a front-end team. He discovered that there were more than 50 different buttons scattered across 20 pages in the code repository:

JSX
// Page A:Red Button
<button style={{ backgroundColor: 'red', color: 'white' }}>
  Delete
</button>

// Page B:Blue Button
<button style={{ backgroundColor: 'blue', color: 'white' }}>
  Submit
</button>

// Page C:Small gray button
<button style={{ backgroundColor: 'gray', color: 'black', fontSize: '12px' }}>
  Cancel
</button>
▶ Try it Yourself

Question:

(2) A Component-Based Approach

Unified management using function components and props:

JSX
function Button({ variant = 'primary', size = 'medium', children }) {
  const styles = {
    primary: { backgroundColor: '#1890ff', color: 'white' },
    danger:  { backgroundColor: '#ff4d4f', color: 'white' },
    default: { backgroundColor: '#f0f0f0', color: '#333' }
  }

  return (
    <button style={{
      ...styles[variant],
      padding: size === 'large' ? '12px 24px' : '8px 16px',
      border: 'none',
      borderRadius: '6px',
      cursor: 'pointer'
    }}>
      {children}
    </button>
  )
}

// Usage:Done in one line of code
<Button variant="danger">Delete</Button>
<Button variant="primary">Submit</Button>
<Button variant="default">Cancel</Button>
▶ Try it Yourself

Benefits: The number of buttons has been reduced from 50 fragments to 1 component; design changes now require only one modification, ensuring a consistent style.



3. Fundamentals of Function Components

(1) Definition of a Component

In React, a function component is a JavaScript function that returns JSX.

JSX
// The Simplest Function Component
function Welcome() {
  return <h1>Hello, React!</h1>
}

// How to Write Arrow Functions(More common)
const Welcome = () => {
  return <h1>Hello, React!</h1>
}

// Implicit Return(Only one line JSX "when " can be omitted return)
const Welcome = () => <h1>Hello, React!</h1>
▶ Try it Yourself
Rule Description
Capitalize the first letter Welcomewelcome ❌ (React distinguishes between components and HTML tags)
Return JSX Must return a JSX element (or null)
Pure Functions The same input (Props) must return the same output (UI)
Props Cannot Be Modified Props are read-only and cannot be modified within the component

(2) Props are function arguments

Props (short for "Properties") are read-only data passed from a parent component to a child component.

JSX
// ---- Define a Component ----
function Greeting(props) {
  // props It is an object,Includes all incoming parameters
  return <h1>Hello,{props.name}!This year, you {props.age} years old。</h1>
}

// ---- Using Components ----
<Greeting name="Alice" age={28} />

// Rendering Results:
// <h1>Hello,Alice!This year, you 28 years old。</h1>
▶ Try it Yourself

(3) Destructuring Syntax for Props

A more common approach is deconstructive assignment, which directly retrieves the desired parameters:

JSX
// Deconstructive Writing Style(Recommendations)
function Greeting({ name, age }) {
  return <h1>Hello,{name}!This year, you {age} years old。</h1>
}

// equivalent to:
function Greeting(props) {
  const { name, age } = props
  return <h1>Hello,{name}!This year, you {age} years old。</h1>
}
▶ Try it Yourself

4. Types of Props

Prop Type Passing Syntax Example of Receiving Notes
String name="Alice" { name } → "Alice" Enclosed in double quotes
Number age={30} { age } → 30 Must use {}
Boolean active / active={true} { active } → true Omit value defaults to true
Object info={{ key: 'val' }} { info } → { key: 'val' } Double curly braces
Array items={[1, 2, 3]} { items } → [1,2,3] Must use {}
Function onClick={handler} { onClick } → fn Callback/Event Handler
Component icon={<Icon />} { icon } → JSX Dynamically Rendered Component

Props can accept any JavaScript data type: strings, numbers, booleans, objects, arrays, functions, and even other components.

(1) String

Strings can be passed directly using double quotes (just like HTML attributes):

JSX
function UserCard({ name, role }) {
  return (
    <div className="card">
      <h2>{name}</h2>
      <p>Characters:{role}</p>
    </div>
  )
}

// Strings are enclosed in double quotes,For other types {} Package
<UserCard name="Alice" role="Administrator" />
▶ Try it Yourself

(2) Numbers, Boolean Values, and Objects

Non-string types must be enclosed in {}:

JSX
function Product({ name, price, inStock, details }) {
  return (
    <div className="product">
      <h3>{name}</h3>
      <p>Price:${price}</p>
      {/* Boolean values are used for conditional rendering */}
      {inStock ? <span>In stock ✅</span> : <span>Out of stock ❌</span>}
      {/* Object:Accessible properties */}
      <p>Category:{details.category} | Weight:{details.weight}g</p>
    </div>
  )
}

// Using Components
<Product
  name="Wireless Mouse"
  price={89}
  inStock={true}
  details={{ category: 'Peripherals', weight: 120 }}
/>
▶ Try it Yourself

(3) Quick Reference Table for Pass-by-Value Methods

Type Example Description
String name="Alice" Written the same way as HTML attributes
Number count={42} Requires {}
Boolean value true active or active={true} If only the property name is specified, the default is true
Boolean value false active={false} Requires {}
Object data={{ x:1, y:2 }} Double Braces
Array items={[1,2,3]} Requires {}
Expression result={a + b} Any JS expression
Variable name={userName} Pass-by-value variable

▶ Example: User Information Card

Output:

TEXT 📖 Display only
Form with input fields and submit handling
JSX
// ============================================
// Example:use  Props Build a User Information Card
// Features:Display Props Transmission of Various Types
// ============================================

function UserCard({ name, age, isOnline, avatar, tags }) {
  return (
    <div style={{
      border: '1px solid #ddd',
      borderRadius: '8px',
      padding: '20px',
      maxWidth: '300px'
    }}>
      <img
        src={avatar}
        alt={name}
        style={{ width: '80px', height: '80px', borderRadius: '50%' }}
      />
      <h2>{name}</h2>
      <p>Age:{age} years old</p>
      <p>
        Status:
        <span style={{ color: isOnline ? '#52c41a' : '#999' }}>
          {isOnline ? 'Online' : 'Offline'}
        </span>
      </p>
      <div>
        Tags:
        {tags.map(tag => (
          <span key={tag} style={{
            background: '#f0f0f0',
            padding: '2px 8px',
            borderRadius: '4px',
            margin: '0 4px',
            fontSize: '12px'
          }}>
            {tag}
          </span>
        ))}
      </div>
    </div>
  )
}

// Using Components
<UserCard
  name="Alice"
  age={28}
  isOnline={true}
  avatar="https://i.pravatar.cc/80"
  tags={['Front End', 'React', 'TypeScript']}
/>

Output:

TEXT 📖 Display only
User card: avatar, "Alice", "Age: 28 years old", status "Online" (green), tags: [Front End] [React] [TypeScript]


5. children and Default Values

(1) children: nested content within the component

children is a special Prop that represents the content between component tags.

JSX
// Definition:use  children Content between tags
function Card({ children, title }) {
  return (
    <div style={{ border: '1px solid #ddd', padding: '16px', borderRadius: '8px' }}>
      <h3>{title}</h3>
      <div>{children}</div>  {/* The content between the tags is displayed here */}
    </div>
  )
}

// Usage:The content between the tags automatically becomes children
<Card title="Announcement">
  <p>The system will go live tonight 22:00 Perform maintenance,Expected to continue 2 hours。</p>
  <p>Please save your work in advance.,To prevent data loss。</p>
</Card>

// Rendering Results:
// <div class="card">
//   <h3>Announcement</h3>
//   <div>
//     <p>The system will go live tonight 22:00 Perform maintenance...</p>
//     <p>Please save your work in advance....</p>
//   </div>
// </div>
▶ Try it Yourself

(2) Default Value

Use ES6 destructuring syntax with default values to set default values for props:

JSX
function Button({ text = 'Click', color = 'blue', size = 'medium' }) {
  return (
    <button style={{
      backgroundColor: color,
      padding: size === 'large' ? '12px 24px' : '8px 16px',
      color: 'white',
      border: 'none',
      borderRadius: '4px'
    }}>
      {text}
    </button>
  )
}

// No parameters → Use the default values
<Button />                    // Blue、medium、"Click"

// Pass some parameters → Override the default value
<Button text="Submit" />        // Blue、medium、"Submit"
<Button color="red" size="large" />  // Red、large、"Click"
▶ Try it Yourself

6. TypeScript Type Annotations (Overview)

Using TypeScript, you can define strict types for props, enabling the editor to provide autocomplete suggestions and perform type checks:

TSX
// ---- Definition Props Type ----
interface ButtonProps {
  text: string           // Required:string
  color?: string         // Optional: add ? suffix
  size?: 'small' | 'medium' | 'large'  // Optional: limited to 3 values
  onClick: () => void    // Required:Function Types
  disabled?: boolean     // Optional: boolean, defaults to false
}

// ---- Using in a component ----
function Button({ text, color = 'blue', size = 'medium', onClick, disabled = false }: ButtonProps) {
  return (
    <button
      onClick={onClick}
      disabled={disabled}
      style={{
        backgroundColor: color,
        padding: size === 'large' ? '12px 24px' : '8px 16px',
        opacity: disabled ? 0.5 : 1,
        color: 'white',
        border: 'none',
        borderRadius: '4px'
      }}
    >
      {text}
    </button>
  )
}

// Usage:The editor provides smart suggestions
<Button
  text="Save"
  color="#1890ff"
  size="medium"
  onClick={() => alert('Saved successfully!')}
/>

// ❌ Error:TypeScript It will throw an error
<Button text="Test" />  // Required field is missing onClick
<Button text="Test" size="xl" onClick={fn} />  // size Outside the permitted range


7. Complete Example: Product Card Component

TSX
// ============================================
// Example:Product Card Component(Comprehensive Application Props Various Types)
// ============================================

// Type Definitions
interface Product {
  id: number
  name: string
  price: number
  image: string
  category: string
  tags: string[]
}

interface ProductCardProps {
  product: Product
  onAddToCart: (productId: number) => void
  showDiscount?: boolean
  size?: 'small' | 'large'
}

// Component Implementation
function ProductCard({
  product,
  onAddToCart,
  showDiscount = false,
  size = 'large'
}: ProductCardProps) {
  return (
    <div style={{
      border: '1px solid #eee',
      borderRadius: '8px',
      padding: size === 'large' ? '16px' : '10px',
      width: size === 'large' ? '280px' : '200px'
    }}>
      <img
        src={product.image}
        alt={product.name}
        style={{ width: '100%', height: '150px', objectFit: 'cover' }}
      />
      <h3>{product.name}</h3>
      <p style={{ color: '#ff4d4f', fontSize: '20px', fontWeight: 'bold' }}>
        ${product.price}
        {showDiscount && <span style={{ fontSize: '12px', color: '#999', textDecoration: 'line-through', marginLeft: '8px' }}>${(product.price * 1.2).toFixed(0)}</span>}
      </p>
      <p style={{ color: '#666', fontSize: '14px' }}>{product.category}</p>
      <div>
        {product.tags.map(tag => (
          <span key={tag} style={{ background: '#f0f5ff', color: '#1890ff', padding: '2px 6px', borderRadius: '4px', margin: '0 2px', fontSize: '12px' }}>
            {tag}
          </span>
        ))}
      </div>
      <button
        onClick={() => onAddToCart(product.id)}
        style={{
          width: '100%',
          padding: '8px',
          marginTop: '10px',
          backgroundColor: '#1890ff',
          color: 'white',
          border: 'none',
          borderRadius: '4px',
          cursor: 'pointer'
        }}
      >
        Add to Cart
      </button>
    </div>
  )
}

// Using Components
const myProduct = {
  id: 101,
  name: 'Mechanical Keyboard',
  price: 299,
  image: 'https://via.placeholder.com/280x150',
  category: 'Computer Peripherals',
  tags: ['Hot Seller', 'New Products']
}

<ProductCard
  product={myProduct}
  onAddToCart={(id) => console.log('Add to Cart:', id)}
  showDiscount={true}
/>

Expected Output: A product card displaying an image of a keyboard, the name "Mechanical Keyboard," the price $299 (strikethrough indicating the original price of $359), the category "Computer Peripherals," and the tags "Hot Seller" and "New Arrival," with a blue "Add to Cart" button below.


▶ Example 2: Comprehensive Use of Props Type Passing and Default Values

Output:

TEXT 📖 Display only
Subheading: "{name}". Displays: "Status:"
JSX
// ============================================
// Example:Profile Card——Props Combined Use of Types and Default Values
// Features:Display string、number、boolean、object、function etc. Props Type Passing
// ============================================

function ProfileCard({
  name = 'Anonymous User',
  age = 0,
  isActive = false,
  avatar = 'https://via.placeholder.com/60',
  skills = [],
  onGreet
}) {
  return (
    <div style={{
      border: '1px solid #e8e8e8',
      borderRadius: '12px',
      padding: '20px',
      maxWidth: '320px',
      boxShadow: '0 2px 8px rgba(0,0,0,0.06)'
    }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: '16px', marginBottom: '16px' }}>
        <img
          src={avatar}
          alt={name}
          style={{ width: '60px', height: '60px', borderRadius: '50%', objectFit: 'cover' }}
        />
        <div>
          <h3 style={{ margin: 0 }}>{name}</h3>
          <p style={{ margin: '4px 0 0 0', color: '#666', fontSize: '14px' }}>
            {age} years old · {isActive ? '🟢 Online' : '🔴 Offline'}
          </p>
        </div>
      </div>

      {skills.length > 0 && (
        <div style={{ marginBottom: '12px' }}>
          <p style={{ margin: '0 0 8px 0', fontWeight: 'bold', fontSize: '14px' }}>Skills:</p>
          <div style={{ display: 'flex', flexWrap: 'wrap', gap: '6px' }}>
            {skills.map((skill, index) => (
              <span key={index} style={{
                backgroundColor: '#f0f5ff',
                color: '#1890ff',
                padding: '4px 10px',
                borderRadius: '12px',
                fontSize: '12px'
              }}>
                {skill}
              </span>
            ))}
          </div>
        </div>
      )}

      {onGreet && (
        <button
          onClick={() => onGreet(name)}
          style={{
            width: '100%',
            padding: '8px',
            backgroundColor: '#1890ff',
            color: 'white',
            border: 'none',
            borderRadius: '6px',
            cursor: 'pointer',
            fontSize: '14px'
          }}
        >
          Say hello to {name}
        </button>
      )}
    </div>
  )
}

// Usage:Pass all parameters
<ProfileCard
  name="Alice"
  age={28}
  isActive={true}
  avatar="https://i.pravatar.cc/60"
  skills={['React', 'TypeScript', 'Node.js']}
  onGreet={(name) => alert(`Hello,${name}!`)}
/>

// Usage:Pass only some parameters(Use the default values for the rest)
<ProfileCard
  name="Bob"
  skills={['Vue', 'Python']}
/>

// Usage:Do not pass any parameters(Use all default values)
<ProfileCard />

Output:

TEXT 📖 Display only
Alice's profile: avatar, "28 years old · 🟢 Online", skills [React][TypeScript][Node.js], "Say hello to Alice" button → alert("Hello, Alice!")

▶ Example 3: Event Callback Props—Child Components Notifying Parent Components

Output:

TEXT 📖 Display only
Displays "products". state: adding, cart. buttons: {adding ? 'adding...' : `add ${productname}`}
JSX
function AddToCartButton({ productName, onAdd }) {
  const [adding, setAdding] = useState(false)

  async function handleClick() {
    setAdding(true)
    await onAdd(productName)
    setAdding(false)
  }

  return (
    <button onClick={handleClick} disabled={adding}
      style={{ padding: '8px 16px', cursor: adding ? 'wait' : 'pointer' }}>
      {adding ? 'Adding...' : `Add ${productName}`}
    </button>
  )
}

function ProductPage() {
  const [cart, setCart] = useState([])

  function handleAdd(name) {
    setCart(prev => [...prev, name])
  }

  return (
    <div>
      <h2>Products</h2>
      <AddToCartButton productName="Keyboard" onAdd={handleAdd} />
      <AddToCartButton productName="Mouse" onAdd={handleAdd} />
      <p>Cart: {cart.join(', ') || 'empty'}</p>
    </div>
  )
}
▶ Try it Yourself

Output:

TEXT 📖 Display only
"Add Keyboard" button → "Adding..." then cart shows "Keyboard". "Add Mouse" button → cart: "Keyboard, Mouse". Empty cart shows "empty".

▶ Example 4: Conditional Rendering of Props—Dynamic Component Switching

Output:

TEXT 📖 Display only
Subheading: "Products". Displays: "Products". State: adding (setter: setAdding), cart (setter: setCart). Button: {adding ? 'Adding...' : `Add ${productName}`}. Async data fetching/loading states
JSX
function StatusMessage({ status }) {
  const config = {
    loading: { icon: '⏳', color: '#1890ff', text: 'Loading...' },
    success: { icon: '✅', color: '#52c41a', text: 'Success!' },
    error:   { icon: '❌', color: '#ff4d4f', text: 'Error occurred' },
    empty:   { icon: '📭', color: '#999', text: 'No data' },
  }
  const { icon, color, text } = config[status] || config.empty

  return (
    <div style={{ padding: 16, textAlign: 'center', color }}>
      <span style={{ fontSize: 32 }}>{icon}</span>
      <p>{text}</p>
    </div>
  )
}

function DataViewer() {
  const [status, setStatus] = useState('loading')
  return (
    <div style={{ maxWidth: 300, margin: '0 auto' }}>
      <StatusMessage status={status} />
      <div style={{ display: 'flex', gap: 4 }}>
        {['loading', 'success', 'error', 'empty'].map(s => (
          <button key={s} onClick={() => setStatus(s)}
            style={{ padding: '4px 8px', cursor: 'pointer', fontSize: 12 }}>
            {s}
          </button>
        ))}
      </div>
    </div>
  )
}
▶ Try it Yourself

Output:

TEXT 📖 Display only
⏳ Loading... (blue) / ✅ Success! (green) / ❌ Error occurred (red) / 📭 No data (gray). Buttons switch status.

▶ Example 5: The renderProps Pattern—List Components and Custom Rendering

Output:

TEXT 📖 Display only
Displays "users"
JSX
function DataList({ items, renderItem, keyExtractor, emptyMessage = 'No items' }) {
  if (!items || items.length === 0) {
    return <p style={{ color: '#999', textAlign: 'center', padding: 20 }}>{emptyMessage}</p>
  }

  return (
    <ul style={{ listStyle: 'none', padding: 0 }}>
      {items.map(item => (
        <li key={keyExtractor(item)} style={{ borderBottom: '1px solid #f0f0f0', padding: 8 }}>
          {renderItem(item)}
        </li>
      ))}
    </ul>
  )
}

function App() {
  const users = [
    { id: 1, name: 'Alice', email: 'alice@example.com', active: true },
    { id: 2, name: 'Bob', email: 'bob@example.com', active: false },
    { id: 3, name: 'Charlie', email: 'charlie@example.com', active: true },
  ]

  const products = [
    { id: 'a', name: 'Keyboard', price: 79 },
    { id: 'b', name: 'Monitor', price: 399 },
  ]

  return (
    <div style={{ maxWidth: 400, margin: '0 auto' }}>
      <h3>Users</h3>
      <DataList
        items={users}
        keyExtractor={u => u.id}
        renderItem={u => (
          <div style={{ display: 'flex', justifyContent: 'space-between' }}>
            <span>{u.name} ({u.email})</span>
            <span style={{ color: u.active ? '#52c41a' : '#999' }}>
              {u.active ? 'Active' : 'Inactive'}
            </span>
          </div>
        )}
      />
      <h3>Products</h3>
      <DataList
        items={products}
        keyExtractor={p => p.id}
        emptyMessage="No products available"
        renderItem={p => (
          <span>{p.name} - ${p.price}</span>
        )}
      />
    </div>
  )
}

Output:

TEXT 📖 Display only
Users: Alice (alice@example.com, Active), Bob (bob@example.com, Inactive), Charlie (charlie@example.com, Active)
Products: Keyboard - $79, Monitor - $399

❓ FAQ

Q Why are props read-only? Can’t they be modified directly?
A React’s design philosophy is “unidirectional data flow”—data flows from parent components to child components, and child components cannot modify the parent’s data. If a child component needs to modify data, it should notify the parent component to do so via a callback function passed through props (to maintain a “single source of truth”). This ensures a clear data flow, making debugging and maintenance easier.
Q What is the difference between props and state?
A Props are passed in from outside and are read-only; state is managed internally by the component and is mutable. You can think of props as the component’s “configuration parameters” and state as its “internal state.” If props change, the component re-renders; if state changes, the component re-renders.
Q Do component names have to be in uppercase? What happens if they’re in lowercase?
A They must be in uppercase. React distinguishes between components and HTML tags based on capitalization: <div> is an HTML div tag; <Div> is a custom component. If a component is written in lowercase, React will treat it as an HTML tag, causing the rendering to fail.
Q How deep can components be nested? Are there any limits?
A There are no technical limits, but we recommend not exceeding 3–4 levels. Excessively deep nesting can complicate data passing (props drilling) and reduce code readability. If you need to pass data across multiple levels, consider using the Context API (Lesson 13) or global state management (Lesson 18).
Q How do I set default values for props?
A There are two ways: ① Assign default values during destructuring function Card({ title = 'Untitled' }) {}; ② Use Card.defaultProps = { title: 'Untitled' }. Note that React no longer recommends using defaultProps (in function components); it is recommended to use destructuring to assign default values. In TypeScript, you can also use title?: string to indicate that a prop is optional, which makes the code clearer when combined with destructuring to assign default values.

📖 Summary


📝 Exercises

  1. Basic Problem (Difficulty ⭐): Create a Avatar component that accepts two props—name (a string) and size (a number, default 50)—and displays the user’s avatar and name.
  2. Advanced Problem (Difficulty ⭐⭐): Create a TagList component that accepts two props—tags (an array of strings) and onRemove (a function)—and renders a list of tags. Clicking a tag deletes it.
  3. Challenge (Difficulty ⭐⭐⭐): Create a DataTable component in TypeScript that accepts columns (an array of column definitions) and data (an array of data) and renders a simple data table. Try adding a striped (Boolean, optional) prop to control the zebra pattern.
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏