What Is the Difference Between State and Props in React JS?

Props are immutable inputs passed from parent components, whereas state is mutable data managed internally within a component that triggers re-renders when updated.

Understanding the difference between state and props in React JS is essential for mastering component architecture in the facebook/react repository. While both determine what appears on the screen, props represent external configuration flowing downward, and state represents internal memory that evolves over time based on user interactions or side effects. React enforces strict separation between these concepts through specific implementation details in the reconciler and component base classes.

Core Definitions

Props: The Immutable Public API

Props (short for "properties") are JavaScript objects containing attributes passed from a parent component to its children. According to packages/react/src/jsx/ReactJSXElement.js, React explicitly freezes the props object using Object.freeze(element.props) immediately after element creation. This architectural choice enforces immutability, ensuring that receiving components cannot modify their own props and must treat them as read-only configuration.

State: The Mutable Internal Store

State represents data owned and managed by the component itself. In packages/react/src/ReactBaseClasses.js, the Component base class initializes this.state and provides the setState method, which merges partial state updates and schedules re-renders. Unlike props, state is writable and persists across renders on the component's associated fiber node in React's internal tree.

Architectural Implementation

The React source code hardcodes distinct lifecycles and update mechanisms for these data types.

Props Enforcement via Object Freezing

When JSX transforms into ReactElement objects, the constructor captures the props object and freezes it to prevent accidental mutations. This immutability allows React to perform efficient reference comparisons during reconciliation, knowing that props changes can only originate from parent updates, not internal modifications.

State Updates Through the Fiber Architecture

State lives on the fiber node associated with each component instance. When setState is called in class components—or when the updater function from useState is invoked in function components—React marks the fiber as needing an update. The reconciler then processes this state change, computes the new component output, and commits the updates to the DOM.

Practical Code Examples

Props: Parent-to-Child Data Flow

Props configure components from the outside and remain read-only within the child.

// Parent.jsx
function Parent() {
  const handleClick = () => alert('Clicked!');
  return <Child label="Press me" onClick={handleClick} />;
}

// Child.jsx
function Child({ label, onClick }) {
  // Props are destructured but cannot be modified here
  return <button onClick={onClick}>{label}</button>;
}

The label and onClick values originate in Parent and pass to Child as props. The Child component cannot modify these values; it consumes them purely for rendering and event delegation.

State: Internal Component Memory

State handles data that changes due to internal logic or user interaction.

// Counter.jsx
import { useState } from 'react';

export default function Counter() {
  const [count, setCount] = useState(0);

  const increment = () => setCount(c => c + 1);

  return (
    <div>
      <p>Current count: {count}</p>
      <button onClick={increment}>Add</button>
    </div>
  );
}

Here, count lives in state, initialized via the useState hook. Each invocation of setCount triggers React's update cycle, scheduling a re-render that displays the incremented value.

Class Component: Explicit Separation

In class components, the distinction appears clearly in the component structure.

// Greeting.jsx
class Greeting extends React.Component {
  constructor(props) {
    super(props);
    this.state = { visible: true };
  }

  toggle = () => this.setState(s => ({ visible: !s.visible }));

  render() {
    const { name } = this.props;      // Immutable external input
    const { visible } = this.state;   // Mutable internal data

    return (
      <div>
        {visible && <h1>Hello, {name}!</h1>}
        <button onClick={this.toggle}>Toggle</button>
      </div>
    );
  }
}

The name prop arrives from the parent and never changes within Greeting, while visible state toggles locally via this.setState.

When to Use Props vs State

Selecting the correct data storage method depends on ownership and mutability requirements.

  • Use Props for data originating outside the component, including configuration values, content strings, or callback functions. Props establish the component's public contract and enable composition patterns.

  • Use State for data that changes over time due to user actions, asynchronous responses, or timer events. State should remain private to the component and not be directly manipulated by parent or sibling components.

Summary

  • Props are immutable objects passed from parent to child, frozen by Object.freeze() in ReactJSXElement.js to prevent mutations, and serve as the component's public API.
  • State is mutable data stored on component fibers, managed via setState or useState, and triggers re-renders when updated through React's scheduler.
  • Prop changes always flow from parent updates, while state changes are controlled internally by the component itself via specific update methods.
  • React implements these patterns in packages/react/src/ReactBaseClasses.js for state management and packages/react/src/jsx/ReactJSXElement.js for props handling.

Frequently Asked Questions

Can a React component modify its own props?

No. React explicitly freezes props objects to enforce immutability. Attempting to modify this.props or argument props directly will not update the UI and may introduce subtle bugs. Components should treat props as read-only and communicate changes back to parents via callback functions passed through props.

What triggers a re-render when using state?

When you call setState in class components or the setter function from useState in function components, React schedules an update for that component's fiber node. During the reconciliation phase, React compares the new state against the previous state, re-renders the component with the updated values, and commits the resulting changes to the DOM.

Should fetched API data live in props or state?

If the component fetches the data itself, store it in state to track loading states and responses. If a parent component fetches the data and passes it down, it arrives via props. For data needed by multiple sibling components, lift the state to their common ancestor or use a dedicated state management solution.

Why does React enforce immutability for props but not state?

Immutability for props ensures predictable top-down data flow, making applications easier to reason about and optimize. Since props originate from parents, allowing child components to modify them would create side effects and complicate debugging. State, however, belongs to the component itself, so React provides specific mechanisms (setState, useState updaters) to mutate it safely while still triggering efficient re-renders.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s https://instagit.com/install.md

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client