reactjs
/

React Components – Building Reusable UI Pieces

Last Sync: Today

On this page

13
0%
5 min read
Remaining
5 minleft

Click any section to jump — progress syncs automatically

reactjs

React Components – Building Reusable UI Pieces

What are React Components?

Components are the building blocks of React applications. They encapsulate UI logic and rendering, making code reusable and maintainable. Components accept inputs called props and return React elements describing what should appear on screen.

Functional Components

React JSXRead-only
1
// Simple functional component
function Welcome({ name }) {
  return <h1>Hello, {name}!</h1>;
}

// Arrow function syntax
const Welcome = ({ name }) => <h1>Hello, {name}!</h1>;

// With multiple elements and logic
function UserCard({ user }) {
  const [isExpanded, setIsExpanded] = useState(false);
  
  return (
    <div className="card">
      <h3>{user.name}</h3>
      <button onClick={() => setIsExpanded(!isExpanded)}>
        {isExpanded ? 'Show less' : 'Show more'}
      </button>
      {isExpanded && (
        <div>
          <p>Email: {user.email}</p>
          <p>Age: {user.age}</p>
        </div>
      )}
    </div>
  );
}

export default Welcome;

Class Components

React JSXRead-only
1
import { Component } from 'react';

// Basic class component
class Welcome extends Component {
  render() {
    return <h1>Hello, {this.props.name}!</h1>;
  }
}

// Class component with state and lifecycle
class Counter extends Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  componentDidMount() {
    console.log('Component mounted');
  }

  componentDidUpdate(prevProps, prevState) {
    if (prevState.count !== this.state.count) {
      console.log(`Count changed to ${this.state.count}`);
    }
  }

  componentWillUnmount() {
    console.log('Component unmounted');
  }

  increment = () => {
    this.setState({ count: this.state.count + 1 });
  };

  render() {
    return (
      <div>
        <p>Count: {this.state.count}</p>
        <button onClick={this.increment}>Increment</button>
      </div>
    );
  }
}

// Class component with props validation
class Button extends Component {
  static defaultProps = {
    variant: 'primary',
    disabled: false
  };

  static propTypes = {
    label: PropTypes.string.isRequired,
    onClick: PropTypes.func.isRequired,
    variant: PropTypes.oneOf(['primary', 'secondary', 'danger'])
  };

  render() {
    const { label, onClick, variant, disabled } = this.props;
    return (
      <button onClick={onClick} disabled={disabled} className={`btn btn-${variant}`}>
        {label}
      </button>
    );
  }
}

Functional vs Class Components

FeatureFunctionalClass
StateuseState hookthis.state
LifecycleuseEffect hookcomponentDidMount, etc.
Code complexitySimple and conciseMore verbose
PerformanceSlightly betterSlightly slower
Learning curve
Easier
Steeper
Current trend✅ RecommendedLegacy (still valid)

Component Composition

React JSXRead-only
1
// Container component
function Container({ children, className }) {
  return <div className={`container ${className}`}>{children}</div>;
}

// Specialized components
function Header({ title, subtitle }) {
  return (
    <header>
      <h1>{title}</h1>
      {subtitle && <p>{subtitle}</p>}
    </header>
  );
}

function Content({ children }) {
  return <main>{children}</main>;
}

function Footer({ year, company }) {
  return <footer>&copy; {year} {company}</footer>;
}

// Composed page component
function Page({ title, subtitle, children, year = 2024, company = 'MyCompany' }) {
  return (
    <Container>
      <Header title={title} subtitle={subtitle} />
      <Content>{children}</Content>
      <Footer year={year} company={company} />
    </Container>
  );
}

// Usage
function App() {
  return (
    <Page title="Welcome" subtitle="This is our app">
      <p>Main content goes here</p>
      <button>Click me</button>
    </Page>
  );
}

Pure Components & React.memo

React JSXRead-only
1
import React, { PureComponent, memo } from 'react';

// Class PureComponent - shallow comparison
class UserCard extends PureComponent {
  render() {
    console.log('Rendering UserCard');
    return <div>{this.props.user.name}</div>;
  }
}

// Functional memo component
const UserCardMemo = memo(function UserCard({ user }) {
  console.log('Rendering UserCardMemo');
  return <div>{user.name}</div>;
});

// Custom comparison function
const UserCardCustom = memo(
  ({ user, onUpdate }) => {
    return <div onClick={() => onUpdate(user.id)}>{user.name}</div>;
  },
  (prevProps, nextProps) => {
    // Return true if props are equal (skip re-render)
    return prevProps.user.id === nextProps.user.id;
  }
);

// Example showing re-render prevention
function App() {
  const [count, setCount] = useState(0);
  const user = { id: 1, name: 'John' };

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Count: {count}</button>
      <UserCard user={user} /> {/* Re-renders every time */}
      <UserCardMemo user={user} /> {/* Only re-renders if user changes */}
    </div>
  );
}

Higher-Order Components (HOC)

React JSXRead-only
1
// HOC for adding loading functionality
function withLoading(Component) {
  return function WithLoading({ isLoading, ...props }) {
    if (isLoading) {
      return <div>Loading...</div>;
    }
    return <Component {...props} />;
  };
}

// Usage
function UserList({ users }) {
  return (
    <ul>
      {users.map(user => <li key={user.id}>{user.name}</li>)}
    </ul>
  );
}

const UserListWithLoading = withLoading(UserList);

// HOC for authentication
function withAuth(Component) {
  return function WithAuth(props) {
    const [isAuthenticated, setIsAuthenticated] = useState(false);

    useEffect(() => {
      checkAuth().then(setIsAuthenticated);
    }, []);

    if (!isAuthenticated) {
      return <LoginPage />;
    }

    return <Component {...props} />;
  };
}

// HOC for logging
function withLogging(Component) {
  return class WithLogging extends React.Component {
    componentDidMount() {
      console.log(`Component ${Component.name} mounted`);
    }

    componentWillUnmount() {
      console.log(`Component ${Component.name} unmounted`);
    }

    render() {
      return <Component {...this.props} />;
    }
  };
}

// Compose multiple HOCs
const EnhancedComponent = withAuth(withLoading(withLogging(UserList)));

Render Props Component

React JSXRead-only
1
// Component that provides data via render prop
class MouseTracker extends Component {
  state = { x: 0, y: 0 };

  handleMouseMove = (e) => {
    this.setState({ x: e.clientX, y: e.clientY });
  };

  render() {
    return (
      <div onMouseMove={this.handleMouseMove}>
        {this.props.render(this.state)}
      </div>
    );
  }
}

// Usage
function App() {
  return (
    <MouseTracker
      render={({ x, y }) => (
        <p>Mouse position: {x}, {y}</p>
      )}
    />
  );
}

// Using children as function
function DataProvider({ url, children }) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch(url)
      .then(res => res.json())
      .then(data => {
        setData(data);
        setLoading(false);
      });
  }, [url]);

  return children({ data, loading });
}

// Usage
<DataProvider url="/api/users">
  {({ data, loading }) => (
    loading ? <Spinner /> : <UserList users={data} />
  )}
</DataProvider>

Compound Components

React JSXRead-only
1
import { createContext, useContext, useState } from 'react';

// Context for sharing state
const TabsContext = createContext();

function Tabs({ children, defaultIndex = 0 }) {
  const [activeIndex, setActiveIndex] = useState(defaultIndex);

  return (
    <TabsContext.Provider value={{ activeIndex, setActiveIndex }}>
      <div className="tabs">{children}</div>
    </TabsContext.Provider>
  );
}

function TabList({ children }) {
  return <div className="tab-list">{children}</div>;
}

function Tab({ index, children }) {
  const { activeIndex, setActiveIndex } = useContext(TabsContext);
  const isActive = activeIndex === index;

  return (
    <button
      className={`tab ${isActive ? 'active' : ''}`}
      onClick={() => setActiveIndex(index)}
    >
      {children}
    </button>
  );
}

function TabPanels({ children }) {
  const { activeIndex } = useContext(TabsContext);
  return <div className="tab-panels">{children[activeIndex]}</div>;
}

function TabPanel({ children }) {
  return <div className="tab-panel">{children}</div>;
}

// Usage - declarative compound components
function App() {
  return (
    <Tabs defaultIndex={0}>
      <TabList>
        <Tab index={0}>Tab 1</Tab>
        <Tab index={1}>Tab 2</Tab>
        <Tab index={2}>Tab 3</Tab>
      </TabList>
      <TabPanels>
        <TabPanel>Content for Tab 1</TabPanel>
        <TabPanel>Content for Tab 2</TabPanel>
        <TabPanel>Content for Tab 3</TabPanel>
      </TabPanels>
    </Tabs>
  );
}

// Tabs component can also be used with dot notation
Tabs.List = TabList;
Tabs.Tab = Tab;
Tabs.Panels = TabPanels;
Tabs.Panel = TabPanel;

Controlled vs Uncontrolled Components

React JSXRead-only
1
// Controlled component - state managed by parent
function ControlledInput({ value, onChange }) {
  return <input value={value} onChange={onChange} />;
}

// Uncontrolled component - internal state
function UncontrolledInput() {
  const [value, setValue] = useState('');
  return <input value={value} onChange={(e) => setValue(e.target.value)} />;
}

// Using ref for uncontrolled
function UncontrolledRefInput() {
  const inputRef = useRef();

  const handleSubmit = () => {
    alert(inputRef.current.value);
  };

  return (
    <div>
      <input ref={inputRef} defaultValue="Initial value" />
      <button onClick={handleSubmit}>Submit</button>
    </div>
  );
}

// Comparison
function App() {
  const [controlledValue, setControlledValue] = useState('');

  return (
    <div>
      <h3>Controlled (parent state)</h3>
      <ControlledInput value={controlledValue} onChange={(e) => setControlledValue(e.target.value)} />
      <p>Value: {controlledValue}</p>

      <h3>Uncontrolled (internal state)</h3>
      <UncontrolledInput />

      <h3>Uncontrolled with ref</h3>
      <UncontrolledRefInput />
    </div>
  );
}

Component Best Practices

  • Single responsibility – Each component should do one thing well
  • Keep components small – Aim for < 200 lines per component
  • Use meaningful names – Component names should describe what they do
  • Props should be minimal – Only pass what the component needs
  • Use composition over inheritance – React has a powerful composition model
  • Extract reusable logic – Use custom hooks instead of HOCs
  • Memoize when necessary – Use React.memo for expensive components
  • Keep components pure – Same props should produce same output
  • Colocate related files – Keep styles, tests with components
  • Document components – Use PropTypes, TypeScript, or comments

Common Component Patterns

PatternUse CaseExample
Container/PresentationalSeparate logic from UIuseFetch hook + Display component
Compound ComponentsRelated components that work togetherTabs, Accordion, Menu
Render PropsShare code between componentsMouse tracker, Data fetcher
Higher-Order ComponentsCross-cutting concernsAuthentication, Logging
Controlled ComponentsForm inputs with parent stateInput, Select, Checkbox
Custom HooksReusable stateful logicuseFetch, useLocalStorage

Conclusion

React components are powerful building blocks for UI development. Start with functional components and hooks for new projects. Use composition to build complex UIs from simple pieces. Choose patterns like HOCs, render props, or custom hooks based on your specific needs.

Try it yourself

import { useState } from 'react';

// Simple functional component
function Button({ onClick, children, variant = 'primary' }) {
  const styles = {
    primary: { backgroundColor: '#007bff', color: 'white' },
    secondary: { backgroundColor: '#6c757d', color: 'white' },
    danger: { backgroundColor: '#dc3545', color: 'white' }
  };

  return (
    <button
      onClick={onClick}
      style={{ ...styles[variant], padding: '10px 20px', margin: '5px', border: 'none', borderRadius: '4px', cursor: 'pointer' }}
    >
      {children}
    </button>
  );
}

// Component composition example
function Card({ title, children }) {
  return (
    <div style={{ border: '1px solid #ddd', borderRadius: '8px', padding: '1rem', margin: '1rem' }}>
      <h3>{title}</h3>
      <div>{children}</div>
    </div>
  );
}

// Compound components pattern
function Toggle({ children }) {
  const [isOn, setIsOn] = useState(false);

  return (
    <div>
      {children({ isOn, toggle: () => setIsOn(!isOn) })}
    </div>
  );
}

// Main app using composition
function App() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <h1>React Components Demo</h1>
      
      <Card title="Button Examples">
        <Button onClick={() => alert('Primary clicked!')}>Primary Button</Button>
        <Button onClick={() => alert('Secondary clicked!')} variant="secondary">Secondary Button</Button>
        <Button onClick={() => alert('Danger clicked!')} variant="danger">Danger Button</Button>
      </Card>

      <Card title="Counter">
        <p>Count: {count}</p>
        <Button onClick={() => setCount(count + 1)}>Increment</Button>
        <Button onClick={() => setCount(count - 1)} variant="secondary">Decrement</Button>
        <Button onClick={() => setCount(0)} variant="danger">Reset</Button>
      </Card>

      <Card title="Toggle Example (Render Props)">
        <Toggle>
          {({ isOn, toggle }) => (
            <div>
              <p>The switch is {isOn ? 'ON' : 'OFF'}</p>
              <Button onClick={toggle} variant={isOn ? 'danger' : 'primary'}>
                {isOn ? 'Turn Off' : 'Turn On'}
              </Button>
            </div>
          )}
        </Toggle>
      </Card>
    </div>
  );
}

export default App;

Test Your Knowledge

Q1
of 4

Which component type is recommended for new React code?

A
Class components
B
Functional components with hooks
C
Pure components
D
Factory components
Q2
of 4

What is React.memo used for?

A
Styling components
B
Memoizing values
C
Preventing re-renders
D
Creating refs
Q3
of 4

What is a higher-order component?

A
A component that renders other components
B
A function that returns a component
C
A component with state
D
A component with lifecycle methods
Q4
of 4

Which pattern is best for related components that share state?

A
Render props
B
Higher-order components
C
Compound components
D
Controlled components

Frequently Asked Questions

Functional vs class components?

Functional components with hooks are recommended for new code. Class components are legacy but still work.

What is React.memo?

A higher-order component that memoizes functional components to prevent unnecessary re-renders.

What is a higher-order component?

A function that takes a component and returns a new component with enhanced functionality.

When to use custom hooks vs HOCs?

Custom hooks are simpler for sharing logic; HOCs are better for augmenting components with additional UI.

Previous

react jsx

Next

react props

Related Content

Need help?

Explore our comprehensive docs or start a chat with our tech experts.