Lesson 4 of 2226 min read

React Fundamentals Every Next.js Developer Must Know

A refresher on JSX, components, props, state, and hooks — the React fundamentals that every Next.js developer relies on daily.

Author: CodersNexus

React Fundamentals Every Next.js Developer Must Know

Next.js is built entirely on top of React, which means every Next.js page, layout, and component you write is, underneath, a React component. If your React fundamentals are shaky, Next.js concepts like Server Components, data fetching, and routing will feel confusing not because they're inherently difficult, but because they're layered on top of React ideas you haven't fully internalized yet.

This lesson is a focused refresher on the five React concepts you will use constantly throughout this course: JSX, components, props, state, and hooks. If you're already comfortable with React, treat this as a quick check to confirm your mental model is solid before moving into Next.js-specific territory.

Learning Objectives

  • Understand JSX and how it differs from HTML.
  • Explain what a React component is and how components compose together.
  • Differentiate between props and state and know when to use each.
  • Use the useState hook to manage local component state.
  • Use the useEffect hook to run side effects in response to changes.

Core Definitions

  • JSX: A syntax extension for JavaScript that lets you write HTML-like markup directly inside JavaScript/TypeScript code, which gets compiled into React function calls.
  • Component: A reusable, self-contained function that returns JSX describing a piece of UI.
  • Props: Short for 'properties'; data passed from a parent component into a child component, read-only from the child's perspective.
  • State: Data that a component manages internally and that can change over time, triggering a re-render when updated.
  • Hook: A special React function (like useState or useEffect) that lets function components use features like state and side effects.
  • useState: A hook that adds local state to a function component, returning the current value and a setter function.
  • useEffect: A hook that runs side effects (like data fetching or subscriptions) in response to component rendering or dependency changes.

Detailed Explanation

JSX looks like HTML but it isn't HTML — it's syntactic sugar that compiles down to JavaScript function calls (React.createElement under the hood). This is why JSX allows embedding JavaScript expressions directly using curly braces, like {user.name}, and why attributes use camelCase (className instead of class, onClick instead of onclick) since they map to JavaScript property names rather than HTML attribute names.

A component is simply a JavaScript function that returns JSX. Function name capitalized, like Button or ProductCard, and React treats it as a reusable UI building block. Components compose: a Page component might render a Navbar component and a ProductList component, which itself renders many ProductCard components — this nesting is the entire mental model of building UI in React.

Props flow data downward, from parent to child, and are read-only within the child. If a ProductCard component receives a prop called price, it can display that price but cannot modify it directly — if the price needs to change, that change must happen in the parent that owns the data.

State is different: it's data a component owns and can change itself, using the useState hook. Calling `const [count, setCount] = useState(0)` gives you a current value (count) and a function to update it (setCount). Calling setCount schedules a re-render with the new value — this is the mechanism that makes React UIs interactive.

useEffect handles side effects — things that happen outside the normal render-and-return flow, like fetching data from an API, setting up an event listener, or synchronizing with a browser API. It runs after render, and its dependency array controls exactly when it re-runs: an empty array [] means 'run once, after the first render'; including a variable means 'run again whenever that variable changes.'

In Next.js's App Router specifically, these hooks (useState, useEffect) can only be used inside Client Components, marked with a 'use client' directive at the top of the file, since interactive state and browser-only effects don't make sense in server-rendered code — a distinction later lessons will cover in depth.

Diagram Description

Visualize a component tree with data flow arrows:

[App Component]
/ | \
[Navbar] [ProductList] [Footer]
|
(maps over products array)
|
[ProductCard] × N

Props flow DOWN: App --price=499--> ProductList --price=499--> ProductCard
State lives LOCALLY: ProductCard manages its own 'isFavorited' state internally via useState

Comparison Table

ConceptOwned ByCan Change?Typical Use
PropsParent componentNo (read-only in child)Passing data/configuration down to children
StateThe component itselfYes, via setter functionTracking values that change over time (form input, toggles)
JSXN/A (syntax)N/ADescribing UI structure inside JS/TS
useStateFunction componentTriggers re-render on changeLocal, interactive component data
useEffectFunction componentRuns after renderSide effects: fetching data, subscriptions, timers

Next.js Practical Example

'use client'; // Required in Next.js App Router for hooks like useState/useEffect

import { useState, useEffect } from 'react';

// A reusable component receiving 'label' via props
function Counter({ label }: { label: string }) {
  // Local state: count starts at 0
  const [count, setCount] = useState(0);

  // Side effect: runs whenever 'count' changes
  useEffect(() => {
    document.title = `${label}: ${count}`;
  }, [count, label]);

  return (
    <div>
      <p>{label}: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

export default function CounterPage() {
  return (
    <main>
      <Counter label="Clicks" />
    </main>
  );
}

CounterPage renders the Counter component and passes it a prop called label with the value "Clicks" — Counter cannot change this value itself, only display it. Inside Counter, useState creates local state (count) that the component owns and can update via setCount whenever the button is clicked, triggering a re-render with the new count. The useEffect hook runs after every render where count or label has changed, updating the browser tab's title — a classic 'side effect' that happens outside the normal JSX-returning render flow. The 'use client' directive at the top is required in the Next.js App Router because useState and useEffect rely on browser interactivity and cannot run in a Server Component.

Industry Examples

  • E-commerce 'Add to Cart' buttons use useState to track a loading or 'added' state locally within the button component before the cart updates globally.
  • Dashboard applications use useEffect to fetch fresh data when a filter or date range prop changes, keeping displayed data in sync with user selections.
  • Form components across nearly every production React and Next.js app use useState to track each input's current value as the user types.
  • Component libraries like design systems (e.g., internal button, modal, and card components) are built entirely around the props pattern, letting teams reuse one component with different data and configuration everywhere.
  • Real-time notification badges use useState combined with useEffect (often alongside WebSockets or polling) to update an unread count as new events arrive.

Interview Questions and Answers

Q1. What is the difference between props and state in React?

Props are data passed from a parent component to a child component and are read-only from the child's perspective — the child cannot modify them directly. State is data that a component manages internally using a hook like useState, and the component itself can update it, which triggers a re-render.

Q2. What is JSX and why can't browsers run it directly?

JSX is a syntax extension that lets you write HTML-like markup inside JavaScript. Browsers cannot execute JSX directly; it must be compiled (typically by a tool like Babel or the Next.js/SWC compiler) into plain JavaScript function calls, such as React.createElement, before it can run.

Q3. What does the useState hook return and how is it typically used?

useState returns an array with two elements: the current state value and a setter function to update it. It's typically used with array destructuring, like `const [count, setCount] = useState(0)`, where calling setCount(newValue) updates the state and triggers a re-render.

Q4. When does useEffect run and how does the dependency array control that?

useEffect runs after the component renders. The dependency array determines when it re-runs: an empty array [] means it runs only once after the initial render; omitting the array means it runs after every render; including specific variables means it re-runs whenever any of those variables change.

Q5. Why are hooks like useState only usable in Client Components in the Next.js App Router?

Hooks like useState and useEffect rely on interactivity and browser APIs that don't exist during server rendering. The App Router requires marking a file with the 'use client' directive to opt into client-side rendering and hook usage; Server Components, which render only on the server, cannot use these hooks.

MCQs With Answers

1. What is JSX?

  1. A CSS preprocessor
  2. A syntax extension that lets you write HTML-like markup in JavaScript
  3. A database query language
  4. A testing framework

Answer: B. A syntax extension that lets you write HTML-like markup in JavaScript

Explanation: JSX lets developers write HTML-like syntax directly inside JavaScript/TypeScript, which is then compiled into JavaScript function calls React understands.

2. Which statement correctly describes props?

  1. Props can be modified by the child component
  2. Props are read-only data passed from parent to child
  3. Props are only used for styling
  4. Props replace the need for state entirely

Answer: B. Props are read-only data passed from parent to child

Explanation: Props flow one-directionally from parent to child and are read-only within the receiving child component.

3. What does calling the setter function returned by useState do?

  1. Deletes the component
  2. Updates the state value and triggers a re-render
  3. Runs a side effect immediately
  4. Changes a prop's value

Answer: B. Updates the state value and triggers a re-render

Explanation: Calling the setter function (e.g., setCount) updates the stored state value and schedules React to re-render the component with the new value.

4. An empty dependency array [] passed to useEffect means:

  1. The effect never runs
  2. The effect runs after every render
  3. The effect runs only once, after the initial render
  4. The effect runs before the component mounts

Answer: C. The effect runs only once, after the initial render

Explanation: An empty dependency array tells React the effect has no dependencies to watch, so it runs a single time after the component's first render.

5. In the Next.js App Router, which directive is required to use hooks like useState?

  1. 'use server'
  2. 'use client'
  3. 'use effect'
  4. No directive is needed

Answer: B. 'use client'

Explanation: The 'use client' directive marks a file as a Client Component, which is required to use interactive hooks like useState and useEffect since Server Components cannot use them.

Common Mistakes to Avoid

  • Trying to modify a prop directly inside a child component instead of lifting the change up to the parent that owns the data.
  • Forgetting the dependency array in useEffect, causing the effect to re-run after every single render unintentionally.
  • Using useState or useEffect inside a Next.js Server Component without adding the 'use client' directive, causing an error.
  • Confusing state updates as synchronous; setCount does not update count immediately within the same function call — it schedules a re-render.
  • Mutating state directly (e.g., pushing to an array stored in state) instead of using the setter function with a new array/object reference.

Interview Notes

  • JSX compiles to JavaScript function calls; it is not valid HTML and cannot run directly in browsers without compilation.
  • Props flow one-way, parent to child, and are read-only in the receiving component.
  • State is owned by the component and updated via a setter function returned from useState, triggering re-renders.
  • useEffect's dependency array controls when the effect re-runs: [] = once, [x] = whenever x changes, omitted = every render.
  • In the Next.js App Router, hooks like useState/useEffect require the 'use client' directive since they need browser interactivity.

Key Takeaways

  • Every Next.js file that returns UI is, at its core, a React component — so React fundamentals directly underpin all Next.js work.
  • Props and state solve different problems: props configure a component from the outside; state tracks what changes from the inside.
  • useState and useEffect are the two hooks you'll reach for constantly to build interactive, data-driven UI.
  • The Client/Server Component distinction in Next.js is built directly on top of which React features (like hooks) a given file needs.

Summary

React fundamentals form the bedrock of everything you'll build in Next.js. JSX lets you describe UI using HTML-like syntax that compiles into JavaScript. Components are reusable functions returning JSX, composed together to build full interfaces. Props pass read-only data from parent to child, while state is data a component owns and updates itself using the useState hook, triggering re-renders. useEffect handles side effects like data fetching, running based on a dependency array that controls exactly when it re-executes. In the Next.js App Router, these interactive hooks require the 'use client' directive, connecting this React refresher directly to Next.js-specific concepts covered in later lessons.

Frequently Asked Questions

No, but you should be comfortable with the fundamentals — JSX, components, props, state, and basic hooks like useState and useEffect. Next.js builds directly on these concepts, so a working understanding makes the framework-specific material much easier to follow.

A React component is a JavaScript function that returns JSX (UI) and follows the convention of being named with a capital letter. React treats capitalized functions returning JSX as components it can render, track, and re-render as needed.

Yes, and this is extremely common. A component can receive configuration data via props from its parent while also managing its own internal, changing data via state — for example, a ProductCard might receive a price prop while managing its own isFavorited state.

In React's Strict Mode (enabled by default in development for App Router projects), effects intentionally run twice to help surface bugs related to improper cleanup. This double-invocation only happens in development, not in production builds.

Omitting the dependency array causes the effect to run after every single render, which can lead to performance issues or infinite loops if the effect itself triggers a state update that causes a re-render.

No. useState can hold any JavaScript value — strings, booleans, objects, arrays, or even functions — making it suitable for form inputs, toggles, fetched data, and much more, not just numeric counters.

The App Router renders components on the server by default. Hooks like useState rely on client-side interactivity that doesn't exist during server rendering, so Next.js requires an explicit 'use client' directive to mark a file as a Client Component before hooks can be used.

Yes. Passing functions as props (often called callback props) is a common pattern that lets a child component notify its parent when something happens, such as a button click, by calling the function the parent passed down.

Re-rendering happens entirely within React's virtual DOM update process when state or props change — it does not reload the browser page or lose other component state. A full page reload restarts the entire application from scratch, which React specifically avoids for the parts of the UI that only need to update.

No. Function components with hooks are the current standard in React and Next.js development. Class components are legacy and still appear in some older codebases, but new Next.js projects are built entirely with function components and hooks.