React Essentials — Components, JSX, Props, State & MoreEntry 5 of 11

Props Are Not Forwarded Automatically

A wrapper component only passes on the props it explicitly uses; everything else is ignored.

Notes

If Examples renders <Section id="examples" /> but Section never reads id, the id is ignored. It does not reach the inner <section> element. Props must be used and set explicitly.

There are two fixes:

  1. Explicit: destructure the prop and set it: <section id={id}>. Fine for one or two props.
  2. Rest and spread (scalable): collect every prop you didn't destructure with ...rest and spread it onto the inner element. Callers can then add id, className, style, aria-*, event handlers, or ref without the wrapper declaring each one.

In React 19, ref is a regular prop, so forwardRef is no longer needed for function components.

What / Why / When

What

Rest and spread props.

Why

Prevents wrappers from swallowing attributes; makes design-system components behave like native elements.

When

Building wrappers around native elements (buttons, inputs, sections).

Use case

A Button component that still accepts type, disabled, and aria-label.

Code compare

React
import type { ComponentProps, ReactNode } from "react";​// ❌ id is ignoredfunction SectionIgnored({ title, children }: { title: string; children: ReactNode }) {  return (    <section>      <h2>{title}</h2>      {children}    </section>  );}​// ✅ Explicit: works, but every prop must be listedfunction SectionExplicit({  title,  id,  children,}: {  title: string;  id?: string;  children: ReactNode;}) {  return (    <section id={id}>      <h2>{title}</h2>      {children}    </section>  );}​// ✅ Rest and spread: forwards everything elsetype SectionProps = ComponentProps<"section"> & { title: string };​export function Section({ title, children, ...rest }: SectionProps) {  return (    <section {...rest}>      <h2>{title}</h2>      {children}    </section>  );}

Attributes placed on a component tag land on the host element (<app-section>) automatically, so nothing is ignored. To make them land on a native element, use an attribute selector so the host is the native element:

Angular
import { Component } from "@angular/core";​@Component({  selector: "button[appButton]",  template: `<ng-content />`,  host: { class: "btn" },})export class ButtonComponent {}

Differences and gotchas

  • React needs explicit props or {...rest}. Angular gets attribute fall-through on the host element for free.
  • An Angular component with an element selector (app-button) wraps an inner <button>, and attributes on <app-button> won't reach that inner button. The attribute selector pattern is the idiomatic fix.

Quick reference

ReactAngular
id={id} or ...rest + {...rest}Attributes fall through to the host element
Wrap a native elementselector: 'button[appButton]'