React Cheatsheet

Props

Use this React reference while you build software engineering projects, review code, or refresh the syntax you reach for most.

Passing Props

Props are passed as JSX attributes and received as the first argument to the component function.

// Parent passes props
<UserCard name="Alice" age={30} active={true} scores={[95, 87]} />

// Child receives props object
function UserCard(props) {
  return <p>{props.name} — {props.age}</p>;
}

// Destructure in the parameter (most common)
function UserCard({ name, age, active, scores }) {
  return <p>{name} — {age}</p>;
}

Prop Types

Any JavaScript value can be a prop:

<Component
  str="hello"                           // string
  num={42}                              // number
  bool={true}                           // boolean (or just: <Component bool />)
  arr={[1, 2, 3]}                       // array
  obj={{ x: 1, y: 2 }}                  // object
  fn={() => console.log('clicked')}     // function
  node={<span>JSX</span>}              // React element
  ref={myRef}                           // ref — a regular prop since React 19
/>

Default Props

// Default values via destructuring (preferred)
function Button({ label = 'Click me', size = 'md', disabled = false }) {
  return <button disabled={disabled} className={`btn-${size}`}>{label}</button>;
}

// Calling component — defaults apply when prop is omitted or undefined
<Button />                    // label="Click me", size="md"
<Button label="Save" />       // size="md", disabled=false
<Button disabled />           // disabled={true}

Component.defaultProps was removed for function components in React 19 — it is silently ignored. Use parameter destructuring defaults (above). defaultProps still works on class components only.

Spreading Props

// Spread an object onto a component
const linkProps = { href: '/about', target: '_blank', rel: 'noreferrer' };
const el = <a {...linkProps}>About</a>;

// Pass through all parent props to a child (forwarding / wrapper components)
function FancyButton({ className, ...rest }) {
  return <button className={`fancy ${className}`} {...rest} />;
}

// Spread + override (later wins)
<Button {...defaults} label="Override" />

PropTypes (legacy — removed in React 19)

React 19 removed propTypes checking from React itself: on function components, a propTypes object is silently ignored. Use TypeScript for type safety in current React. The pattern below only produces runtime warnings on React ≤18.

// React ≤18 only — silently ignored on function components in React 19
import PropTypes from 'prop-types'; // npm install prop-types

function Avatar({ src, size, alt }) {
  return <img src={src} width={size} height={size} alt={alt} />;
}

Avatar.propTypes = {
  src:    PropTypes.string.isRequired,
  size:   PropTypes.number,
  alt:    PropTypes.string,
  onClick: PropTypes.func,
  theme:  PropTypes.oneOf(['light', 'dark']),
  user:   PropTypes.shape({
    id:   PropTypes.number.isRequired,
    name: PropTypes.string,
  }),
  items:  PropTypes.arrayOf(PropTypes.string),
  node:   PropTypes.node,           // anything renderable
  element: PropTypes.element,       // React element
  any:    PropTypes.any,
};

Even on React ≤18, PropTypes only run in development. For real type safety use TypeScript (below).

TypeScript Prop Types

// Inline type
function Button({ label, onClick }: { label: string; onClick: () => void }) {
  return <button onClick={onClick}>{label}</button>;
}

// Named interface (preferred for reuse)
interface CardProps {
  title: string;
  subtitle?: string;          // optional
  children: React.ReactNode;
  onClose: () => void;
  style?: React.CSSProperties;
}

function Card({ title, subtitle, children, onClose }: CardProps) {
  return (
    <div>
      <h2>{title}</h2>
      {subtitle && <p>{subtitle}</p>}
      {children}
      <button onClick={onClose}>Close</button>
    </div>
  );
}

// Type alias
type ButtonProps = {
  variant: 'primary' | 'secondary' | 'danger';
  size?: 'sm' | 'md' | 'lg';
  disabled?: boolean;
};

children Prop

// React.ReactNode — anything React can render
function Wrapper({ children }: { children: React.ReactNode }) {
  return <div className="wrapper">{children}</div>;
}

// React.ReactElement — must be a single React element
function SingleChild({ children }: { children: React.ReactElement }) {
  return React.cloneElement(children, { className: 'injected' });
}

// Function as children (render prop)
function Toggle({ children }: { children: (on: boolean) => React.ReactNode }) {
  const [on, setOn] = useState(false);
  return (
    <>
      {children(on)}
      <button onClick={() => setOn(v => !v)}>Toggle</button>
    </>
  );
}
<Toggle>{on => <span>{on ? 'ON' : 'OFF'}</span>}</Toggle>

Prop Drilling and When to Avoid It

// Prop drilling — passing through intermediaries that don't use it
function Page({ user }) {
  return <Sidebar user={user} />;      // Sidebar doesn't use user…
}
function Sidebar({ user }) {
  return <Avatar user={user} />;       // …only Avatar does
}
function Avatar({ user }) {
  return <img src={user.avatar} alt={user.name} />;
}

// Solutions:
// 1. Composition — pass rendered JSX instead of data
function Page({ user }) {
  return <Sidebar avatar={<Avatar user={user} />} />;
}
function Sidebar({ avatar }) {
  return <aside>{avatar}</aside>;
}

// 2. Context (see Context page)
// 3. State management library (Zustand, Redux Toolkit, Jotai…)

Immutability — Never Mutate Props

// WRONG — never mutate props
function Bad({ items }) {
  items.push({ id: 99 });  // mutates the parent's array
  return <ul>{items.map(...)}</ul>;
}

// CORRECT — derive new values
function Good({ items }) {
  const extended = [...items, { id: 99 }];
  return <ul>{extended.map(...)}</ul>;
}

Gotchas

// Boolean shorthand: <Input required /> is the same as <Input required={true} />

// Passing 0 as a prop still renders! Use !! or Boolean() to guard:
{count && <Badge count={count} />}  // renders "0" when count is 0!
{count > 0 && <Badge count={count} />}  // correct

// Objects/arrays as default props — define outside the component to avoid
// creating a new reference every render (breaks memo):
const DEFAULT_OPTIONS = { size: 'md' };
function Input({ options = DEFAULT_OPTIONS }) { ... }