DEV_NET_CORE
GET_STARTED
ReactComponents, props, state, and rendering behavior

Functional components and JSX composition

Overview

Functional components and JSX composition are the foundation of modern React applications. A functional component is a JavaScript or TypeScript function that returns React elements, usually written with JSX. JSX is a syntax extension that lets developers describe UI with HTML-like markup inside JavaScript.

Code
function WelcomeCard({ name }: { name: string }) {
  return (
    <section className="card">
      <h2>Hello, {name}</h2>
      <p>Welcome back.</p>
    </section>
  );
}

Composition means building larger interfaces by combining smaller components. Instead of creating one large component that knows everything, React encourages breaking UI into focused pieces and nesting them together.

Code
function Dashboard() {
  return (
    <PageLayout>
      <Header />
      <Sidebar />
      <MainContent />
    </PageLayout>
  );
}

This topic matters in interviews because it tests whether a candidate understands React's mental model: UI is a tree of components, components should be pure during rendering, JSX is JavaScript under the hood, and reusable interfaces are built through composition rather than inheritance or direct DOM manipulation.

Strong React developers should be able to explain component naming rules, JSX syntax rules, fragments, children, conditional rendering, list rendering, keys, component extraction, render purity, and when composition is better than configuration-heavy component APIs.

Core Concepts

Functional Components

A functional component is a function that returns React elements.

Code
function ProfileAvatar() {
  return (
    <img
      src="/images/avatar.png"
      alt="User avatar"
    />
  );
}

Components can be reused and nested:

Code
function UserProfile() {
  return (
    <article>
      <ProfileAvatar />
      <h2>Ava Nguyen</h2>
    </article>
  );
}

React component names must start with a capital letter. Lowercase JSX tags are treated as built-in HTML elements.

Code
function button() {
  return <button>Save</button>;
}

function App() {
  return <button />; // HTML button, not the function above.
}

Correct:

Code
function Button() {
  return <button>Save</button>;
}

function App() {
  return <Button />;
}

JSX

JSX lets developers write markup-like syntax inside JavaScript.

Code
function ProductTitle({ name }: { name: string }) {
  return <h1>{name}</h1>;
}

JSX is not a string and not HTML. It is syntax that tools transform into JavaScript calls that describe React elements.

Important JSX rules:

  • A component must return one parent element.
  • Use className instead of class.
  • Use htmlFor instead of for.
  • Close all tags, including self-closing tags.
  • Use camelCase for many DOM attributes and event handlers.
  • Use curly braces for JavaScript expressions.

Example:

Code
function SearchInput({ id }: { id: string }) {
  return (
    <label htmlFor={id}>
      Search
      <input id={id} className="search-input" />
    </label>
  );
}

Returning One Parent Element

JSX expressions must return a single parent element. This is invalid:

Code
function UserHeader() {
  return (
    <h1>Ava</h1>
    <p>Frontend Engineer</p>
  );
}

Wrap the elements in a parent:

Code
function UserHeader() {
  return (
    <header>
      <h1>Ava</h1>
      <p>Frontend Engineer</p>
    </header>
  );
}

Or use a fragment when no extra DOM element is needed:

Code
function UserHeader() {
  return (
    <>
      <h1>Ava</h1>
      <p>Frontend Engineer</p>
    </>
  );
}

Fragments are useful when layout or semantics would be harmed by an unnecessary wrapper element.

JavaScript Expressions in JSX

Curly braces let you embed JavaScript expressions in JSX.

Code
function MessageCount({ count }: { count: number }) {
  return <p>You have {count} unread messages.</p>;
}

You can use expressions, not statements:

Code
function Price({ value }: { value: number }) {
  return <span>${value.toFixed(2)}</span>;
}

Invalid:

Code
function Status({ isOnline }: { isOnline: boolean }) {
  return <p>{if (isOnline) "Online"}</p>;
}

Use a ternary or compute before returning:

Code
function Status({ isOnline }: { isOnline: boolean }) {
  const label = isOnline ? "Online" : "Offline";
  return <p>{label}</p>;
}

Objects cannot be rendered directly as children:

Code
const user = { name: "Ava" };

return <p>{user}</p>; // Error.

Render a property or transform it:

Code
return <p>{user.name}</p>;

JSX Attributes and Props

JSX attributes become props passed to components or DOM attributes for built-in elements.

Code
function Button({ label }: { label: string }) {
  return <button>{label}</button>;
}

function Toolbar() {
  return <Button label="Save" />;
}

For dynamic values, use curly braces:

Code
const isDisabled = formStatus === "submitting";

return <button disabled={isDisabled}>Submit</button>;

Boolean props can be passed with shorthand:

Code
<button disabled>Submit</button>

This is equivalent to:

Code
<button disabled={true}>Submit</button>

Component Composition

Composition means using components inside other components.

Code
function Card({ title, children }: { title: string; children: React.ReactNode }) {
  return (
    <section className="card">
      <h2>{title}</h2>
      <div>{children}</div>
    </section>
  );
}

function Dashboard() {
  return (
    <Card title="Activity">
      <p>No recent activity.</p>
    </Card>
  );
}

This pattern keeps the Card responsible for structure and styling while allowing the caller to provide content.

Composition is useful for:

  • Layout components.
  • Reusable form controls.
  • Dialogs and modals.
  • Cards and panels.
  • Page shells.
  • Feature-specific component trees.

The children Prop

children is a special prop containing whatever is placed between a component's opening and closing tags.

Code
function Alert({ children }: { children: React.ReactNode }) {
  return <div role="alert">{children}</div>;
}

function SaveError() {
  return (
    <Alert>
      <strong>Save failed.</strong> Please try again.
    </Alert>
  );
}

children supports flexible composition because the parent does not need to predict every possible piece of content.

Do not overuse children when named props communicate intent better:

Code
function UserCard({
  avatar,
  title,
  actions,
}: {
  avatar: React.ReactNode;
  title: React.ReactNode;
  actions: React.ReactNode;
}) {
  return (
    <article>
      <div>{avatar}</div>
      <h2>{title}</h2>
      <footer>{actions}</footer>
    </article>
  );
}

This is still composition, but with named slots.

Conditional Rendering

Components can return different JSX based on data.

Code
function LoginButton({ isLoggedIn }: { isLoggedIn: boolean }) {
  if (isLoggedIn) {
    return <button>Log out</button>;
  }

  return <button>Log in</button>;
}

Ternary expressions are useful for small conditions:

Code
function StatusBadge({ online }: { online: boolean }) {
  return <span>{online ? "Online" : "Offline"}</span>;
}

Logical && is useful for optional rendering:

Code
function ErrorMessage({ message }: { message?: string }) {
  return (
    <>
      {message && <p role="alert">{message}</p>}
    </>
  );
}

Be careful with numeric values:

Code
{count && <Badge count={count} />}

If count is 0, React may render 0 instead of rendering nothing. Prefer an explicit condition:

Code
{count > 0 && <Badge count={count} />}

Rendering Lists

Use array methods such as map to render lists.

Code
type User = {
  id: string;
  name: string;
};

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

Every item in a list needs a stable key. The key helps React match items between renders.

Good key:

Code
<li key={user.id}>{user.name}</li>

Risky key:

Code
{users.map((user, index) => (
  <li key={index}>{user.name}</li>
))}

Index keys are risky when items can be inserted, removed, reordered, filtered, or sorted. They can cause incorrect state preservation and confusing UI bugs.

Component Extraction

Extract a component when a piece of UI has a clear responsibility, is repeated, or makes a parent component hard to read.

Before:

Code
function ProductList({ products }: { products: Product[] }) {
  return (
    <ul>
      {products.map((product) => (
        <li key={product.id}>
          <h3>{product.name}</h3>
          <p>${product.price.toFixed(2)}</p>
          <button>Add to cart</button>
        </li>
      ))}
    </ul>
  );
}

After:

Code
function ProductCard({ product }: { product: Product }) {
  return (
    <li>
      <h3>{product.name}</h3>
      <p>${product.price.toFixed(2)}</p>
      <button>Add to cart</button>
    </li>
  );
}

function ProductList({ products }: { products: Product[] }) {
  return (
    <ul>
      {products.map((product) => (
        <ProductCard key={product.id} product={product} />
      ))}
    </ul>
  );
}

Do not extract components mechanically. Too many tiny components can make the code harder to follow. Extract around meaningful responsibilities.

Purity During Rendering

React components should be pure during rendering: given the same inputs, they should return the same JSX and should not mutate values that existed before rendering.

Bad:

Code
let nextId = 0;

function Item() {
  nextId += 1;
  return <li>Item {nextId}</li>;
}

This changes external state during render. It can break when React re-renders, retries, or runs extra development checks.

Better:

Code
function Item({ id }: { id: number }) {
  return <li>Item {id}</li>;
}

Side effects belong in event handlers or effects, not in render logic.

Code
function SaveButton() {
  function handleClick() {
    analytics.track("save_clicked");
  }

  return <button onClick={handleClick}>Save</button>;
}

Component Definitions Should Stay at Top Level

Avoid defining components inside other components.

Code
function Parent() {
  function Child() {
    return <p>Child</p>;
  }

  return <Child />;
}

This creates a new component function every render and can cause state to reset unexpectedly.

Prefer top-level definitions:

Code
function Child() {
  return <p>Child</p>;
}

function Parent() {
  return <Child />;
}

If the child needs data, pass it with props:

Code
function Child({ label }: { label: string }) {
  return <p>{label}</p>;
}

function Parent() {
  return <Child label="Child" />;
}

Composition vs Inheritance

React code usually favors composition over inheritance. Instead of creating a base class or a highly abstract component hierarchy, you combine components and pass data, event handlers, or JSX.

Code
function Dialog({
  title,
  children,
  actions,
}: {
  title: string;
  children: React.ReactNode;
  actions: React.ReactNode;
}) {
  return (
    <section role="dialog" aria-label={title}>
      <h2>{title}</h2>
      <div>{children}</div>
      <footer>{actions}</footer>
    </section>
  );
}

Usage:

Code
<Dialog
  title="Delete project"
  actions={<button>Confirm</button>}
>
  <p>This action cannot be undone.</p>
</Dialog>

Composition keeps behavior explicit at the usage site and avoids deep inheritance chains.

Common Mistakes

Common mistakes include:

  • Naming components with lowercase names.
  • Returning multiple sibling JSX elements without a wrapper or fragment.
  • Forgetting to close tags.
  • Using class instead of className.
  • Using statements instead of expressions inside JSX braces.
  • Rendering objects directly.
  • Defining components inside components.
  • Mutating external values during render.
  • Using array index keys for reorderable lists.
  • Extracting components before there is a clear responsibility.
  • Creating component APIs with too many boolean flags instead of composing smaller pieces.

Best Practices

Use these rules of thumb:

  • Keep components pure during rendering.
  • Name components with PascalCase.
  • Use JSX to describe UI from data.
  • Break components around clear responsibilities.
  • Prefer composition through children, named slots, and smaller components.
  • Use fragments to avoid unnecessary DOM wrappers.
  • Use stable keys for list items.
  • Keep side effects in event handlers or effects.
  • Let parent components coordinate structure while child components handle focused display logic.

Interview Practice

PreviousControlled inputs and event handlingNext UpProps flow, local state, and lifting state up