u/Healthy-Tree-4640

A discussion thread for anyone who's built or used React compound-component libraries (Radix, Headless UI, Mantine, Reach UI, Ariakit, etc.) — interested in how others resolve prop priority when the same configuration can be set in multiple places.

I've been maintaining a small audio-component library and recently added customization knobs (triggerType, placement) to a couple of compound slots. The same value can come from three places:

  1. Compound prop<Player.Volume placement="bottom" />
  2. UIContext at the provider — <Player placement={{ volumeSlider: "bottom" }} />
  3. Component default — hardcoded fallback inside the slot itself

I landed on compound prop > UIContext > component default as the resolution order. Reasoning:

  • Per-instance overrides should win over global defaults (closest-to-the-user wins)
  • UIContext is the "set once, apply to all instances" knob, so it's the middle layer
  • Component default catches the case where neither is set

Representative snippet:

function VolumeSlot({
  triggerType: triggerTypeProp,
  placement: placementProp,
}: VolumeProps) {
  const ctx = useUIContext();
  const triggerType =
    triggerTypeProp ?? ctx.triggerType?.volumeSlider ?? "hover";
  const placement =
    placementProp ?? ctx.placement?.volumeSlider ?? "auto";
  // ...
}

Three specific questions I keep going back and forth on:

  1. Reverse priority for "centralized config wins" — has anyone reached for UIContext > compound prop instead, so app-wide config can lock down per-instance overrides? Feels backwards to me, but I can imagine a design-system team wanting it.

  2. Partial overrides — if the compound prop sets placement but not triggerType, and UIContext sets both, do you merge per-key (current behavior) or treat the compound prop as all-or-nothing? The per-key merge is convenient but makes the resolution path harder to reason about by reading one file.

  3. Multiple instances of the same slot under one provider — if I mount two <Player.Volume /> slots, they share one UIContext entry. Do you key the context value to slot identity (e.g. via id prop) or treat all instances of the same slot type as equivalent? I went with the latter for simplicity but it bites when one instance wants a different default.

Curious how Radix / Headless UI / Mantine / Reach UI / Ariakit internals handle this. If anyone has links to their resolution logic in source, I'd appreciate the pointer.

For context (not the topic of the post): the library is an audio player with a compound-slot layout — https://github.com/slash9494/react-modern-audio-player. The 2.3.1 release notes have the actual API surface this thread is about.

u/Healthy-Tree-4640 — 12 days ago