React Patterns & Best PracticesEntry 5 of 10

Forwarding Props

Passing any extra props a wrapper component receives on to the element it renders, using rest/spread syntax.

Notes

  • Wrapper components (Accordion, Button, Input) shouldn't need a prop declared for every possible attribute (className, id, aria-*, event handlers).
  • Collect the rest with ...props and spread them onto the underlying element.
  • Type them with ComponentPropsWithoutRef<'element'> so TypeScript knows every valid attribute.
  • Destructure the props the component handles itself first, so they aren't forwarded by accident.

What / Why / When

What

Transparent attribute pass-through.

Why

Reusable components stay flexible without a growing prop list.

When

Components that wrap a single native element.

Code compare

React
// components/Accordion/AccordionTitle.tsx (with forwarding)import type { ComponentPropsWithoutRef } from "react";​type AccordionTitleProps = ComponentPropsWithoutRef<"h3">;​export default function AccordionTitle({ children, ...props }: AccordionTitleProps) {  const { toggleItem } = useAccordionContext();  const id = useAccordionItemContext();​  return (    <h3 {...props} onClick={() => toggleItem(id)}>      {children}    </h3>  );}

Attributes such as class, id, title, and aria-* written on a component's tag land on its host element automatically. For attributes the host can't use (form controls), make the component an attribute selector on the native element instead.

Angular
// Attribute selector: the native button IS the component's host@Component({  selector: "button[appButton]",  template: `<ng-content />`,  host: { class: "app-button" },})export class ButtonComponent {}
Angular
<!-- type, disabled, aria-label, (click) all work natively --><button appButton type="submit" [disabled]="isPending()" aria-label="Save">Save</button>

Differences and gotchas

  • In React, place {...props} before props you want to enforce (like onClick above), or after props the consumer should be able to override.
  • Angular has no rest props. Host attributes and attribute selectors cover the same need.

Quick reference

ReactAngular
{ children, ...props } + {...props}Attributes land on the host element
ComponentPropsWithoutRef<'button'>selector: 'button[appButton]'