React components are reusable, self-contained UI building blocks — functional components (plain functions returning JSX) have replaced class components for most use cases since hooks were introduced in React 16.8.
Plain JavaScript functions returning JSX — the modern standard. Hooks provide state, effects, and all features previously exclusive to classes.
ES6 classes with render() and lifecycle methods. More verbose, require this binding, and make logic reuse harder.
Hooks solve three problems: reusing stateful logic (custom hooks vs HOCs), keeping related logic together (useEffect vs split lifecycles), and eliminating this binding.
Error boundaries still require class components (getDerivedStateFromError, componentDidCatch) — no hook equivalent exists yet.
React components compose via children prop and configuration props — never use class inheritance for component reuse.
Components are the fundamental building blocks of every React application. A component is a self-contained piece of UI that accepts inputs (props), manages its own state, and returns JSX describing what should appear on screen.
Functional components are plain JavaScript functions that take a props object and return JSX. They are the modern standard for writing React components.
function Greeting({ name }) {
return <h1>Hello, {name}</h1>;
}Before hooks (React 16.8), functional components were "stateless" — they could only receive props and render UI. Hooks (useState, useEffect, useRef, etc.) gave functional components the ability to manage state, run side effects, and access all features previously exclusive to class components.
Class components are ES6 classes that extend React.Component and must implement a render() method. They have lifecycle methods (componentDidMount, componentDidUpdate, componentWillUnmount) for handling side effects and a this.state/this.setState() API for state management.
class Greeting extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}Class components are more verbose, require understanding of this binding (a common source of bugs), and make it harder to reuse stateful logic between components.
Hooks solved the three main problems with class components:
componentDidMount + componentWillUnmount for the same subscription). useEffect keeps related logic together.this.handleClick = this.handleClick.bind(this) in the constructor). Functional components have no this.Functional components are shorter, easier to read, easier to test, and compose better.
Error boundaries require class components — they use static getDerivedStateFromError() and componentDidCatch(), which have no hook equivalents. For everything else, functional components with hooks are preferred. Libraries like react-error-boundary provide a wrapper so you rarely need to write class components yourself.
React components compose together through several patterns:
<Card><p>Content</p></Card> — Card receives the paragraph as props.children.A pure component always renders the same output for the same props and state — no side effects during rendering. React can optimize pure components by skipping re-renders when props haven't changed (using React.memo for functional components).
Functional components are functions that return JSX and use hooks for state and effects. Class components are ES6 classes with lifecycle methods. Functional components are the modern standard — class components are only needed for error boundaries. Component composition (via children and props) is preferred over inheritance.
Fun Fact
React 16.8 (February 2019) introduced hooks, but the React team considered making functional components the only option. They kept class components for backward compatibility, and the official docs now teach hooks-first — class components are in the 'Legacy APIs' section.