React Cheatsheet

Props

Use this React reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

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 }) { ... }