Posts

Prop Collections & Getters in React: Build More Flexible Hooks & Components

April 25, 2025
In React, providing reusable logic is often done via custom hooks. But when we want to give consumers complete control over rendering, while still encapsulating behavior, we need more powerful patterns. That’s where Prop Collections and Prop Getters come in. These patterns are commonly used in battle-tested libraries like React Hook Form, Downshift, and React Table. Let’s break them down and build something with them. 🚀 Prop Collections are just objects of props you pass down to elements to encapsulate behavior.
Prop Collection Example
const inputProps = {
  onChange: handleChange,
  value,
  placeholder: 'Type here...',
};
You just spread them like this:
Usage Example
<input {...inputProps} />
Simple and useful. Prop Getters are functions that return prop collections. The key benefit: they let users customize behavior without overriding internal logic.
Prop Getter Example
const getInputProps = (userProps) => ({
  onChange: composeHandlers(userProps.onChange, handleChange),
  value,
  ...userProps,
});
Now the user can do:
Usage Example
<input {...getInputProps({ placeholder: 'Search...' })} />
We’ll demonstrate both patterns in action by building a simple toggle hook.
Basic Toggle Hook
import { useState } from 'react';

function useToggle(defaultOn = false) {
  const [on, setOn] = useState(defaultOn);

  const toggle = () => setOn(prev => !prev);

  return { on, toggle };
}
Now let’s expose some prop collections and getters.
Prop Collection Example
function useToggle(defaultOn = false) {
  const [on, setOn] = useState(defaultOn);
  const toggle = () => setOn(prev => !prev);

  // Collection of props for a toggler button
  const togglerProps = {
    'aria-pressed': on,
    onClick: toggle,
  };

  return {
    on,
    toggle,
    togglerProps,
  };
}
Usage:
Usage Example
const { on, togglerProps } = useToggle();

<button {...togglerProps}>
  {on ? 'ON' : 'OFF'}
</button>
Let’s allow the consumer to merge custom props safely:
Prop Getter Example
function useToggle(defaultOn = false) {
  const [on, setOn] = useState(defaultOn);
  const toggle = () => setOn(prev => !prev);

  const getTogglerProps = (props = {}) => ({
    'aria-pressed': on,
    onClick: (...args) => {
      props.onClick?.(...args);
      toggle();
    },
    ...props,
  });

  return {
    on,
    toggle,
    getTogglerProps,
  };
}
Now usage becomes:
Usage Example
const { on, getTogglerProps } = useToggle();

<button
  {...getTogglerProps({
    onClick: () => console.log('Clicked!'),
    className: 'my-btn',
  })}
>
  {on ? 'ON' : 'OFF'}
</button>
Internals stay safe, and users get full freedom.
  • 🧩 Encapsulation: You define logic, users define rendering.
  • 🎛️ Composability: Easy to use in any UI framework.
  • 🚦 Safety: Avoid prop collisions and handler overrides.
  • 🧠 Developer Experience: Consumers get full control with sensible defaults.
React Hook Form Example
const { register } = useForm();
<input {...register('email')} />
Behind the scenes, register is a prop getter that wires up onChange, onBlur, and ref.
Downshift Example
const { getInputProps } = useCombobox();
<input {...getInputProps({ placeholder: 'Search' })} />
This gives you complete control over rendering the UI while keeping logic in sync. Use Prop Collections and Prop Getters when:
  • You’re building a custom hook or headless UI library.
  • You want to give users render control but preserve behavioral integrity.
  • You're managing event handlers like onChange, onClick, onBlur, etc.
These patterns may look simple, but they scale incredibly well—especially in hooks-based component libraries. If you're creating flexible, reusable components or libraries, consider embracing Prop Collections and Prop Getters. They might just be your new best friends.
On this page