An open API service indexing awesome lists of open source software.

https://github.com/walter0b/react-cmp-selector

An npm package designed to simplify component selection in React applications
https://github.com/walter0b/react-cmp-selector

cmp npm package react

Last synced: about 1 month ago
JSON representation

An npm package designed to simplify component selection in React applications

Awesome Lists containing this project

README

          

# react-cmp-selector

A powerful and extensible utility for selecting and manipulating React components based on attributes. Ideal for dynamic layouts, slotted rendering, and component injection patterns.

---

## Table of Contents

- [react-cmp-selector](#react-cmp-selector)
- [Table of Contents](#table-of-contents)
- [Installation](#installation)
- [Motivation](#motivation)
- [Table of Proposals](#table-of-proposals)
- [Usage](#usage)
- [1. Basic Slot Matching](#1-basic-slot-matching)
- [2. Matching Multiple Components](#2-matching-multiple-components)
- [3. Combining Event Handlers](#3-combining-event-handlers)
- [4. Declarative API with ``](#4-declarative-api-with-slot)
- [5. Fallback Slot Content](#5-fallback-slot-content)
- [6. Slot Markers](#6-slot-markers)
- [7. Slot Validation](#7-slot-validation)
- [Use Cases](#use-cases)
- [API Reference](#api-reference)
- [`getCmpByAttr

()`](#getcmpbyattrp)
- [`ComponentFinderProps`](#componentfinderprops)
- [`Slot`](#slot)
- [`SlotUtils`](#slotutils)
- [Caveats](#caveats)
- [License](#license)

---

## Installation

```bash
npm install react-cmp-selector
```

or using yarn:

```bash
yarn add react-cmp-selector
```

---

## Motivation

React does not support named slots or dynamic selection of children out of the box. This utility solves that by providing:

- A **hook** to search through children by attribute
- A **component-based API** (``) for declarative slot usage
- Tools to **inject props**, **merge handlers**, and **validate layout contracts**

---

## Table of Proposals

| Feature | Prop / Option | Type | Description |
| ----------------------------- | -------------------------- | --------------------------------- | ------------------------------------------------------------- |
| **Attribute Matching** | `attribute` | `string` | Attribute name to search for (default: `'data-slot'`) |
| **Value Matching** | `value` / `name` | `string` | The value to match against the selected attribute |
| **Prop Merging** | `props` | `Partial

` | Injected props to merge into the matched component(s) |
| **Function Merging Strategy** | `functionPropMerge` | `'combine' \| 'override'` | Defines how function props like `onClick` should be merged |
| **Debug Mode** | `debug` | `boolean` | Enables logging of matching and merging behavior |
| **Match All** | `findAll` | `boolean` | If true, returns all matching components instead of the first |
| **Hook Interface** | `getCmpByAttr()` | — | Programmatic interface to extract and modify children |
| **Declarative API** | `` | — | React component alternative to the hook |
| **Slot Markers** | `SlotUtils.createMarker()` | — | Creates a named slot wrapper component |
| **Slot Validation** | `SlotUtils.validate()` | — | Dev-only validation for required slot presence |
| **Fallback Rendering** | `fallback` | `ReactNode` | Rendered if no matching slot is found |
| **onFound Callback** | `onFound` | `(element: ReactElement) => void` | Runs when a match is found (e.g. for side effects) |

---

## Usage

### 1. Basic Slot Matching

**Children**

```tsx
export function ChildComponents() {
return (
<>

Header

Body

Footer

>
);
}
```

**Parent**

```tsx
const header = getCmpByAttr({
children: ,
value: "header",
props: { className: "highlighted" },
});
```

**Output**

```html

Header

```

**How It Works**

- `getCmpByAttr()` searches children for `data-slot="header"`.
- The match is cloned with the `className` prop added.

---

### 2. Matching Multiple Components

**Children**

```tsx
function Buttons() {
return (
<>
Save
Cancel
>
);
}
```

**Parent**

```tsx
const buttons = getCmpByAttr({
children: ,
attribute: "data-role",
value: "action-button",
findAll: true,
props: { "data-tracked": true },
});
```

**Output**

```html
Save
Cancel
```

---

### 3. Combining Event Handlers

**Children**

```tsx
function CTA() {
return (
console.log("child")}>
Click Me

);
}
```

**Parent**

```tsx
const cta = getCmpByAttr({
children: ,
value: "cta",
props: {
onClick: () => console.log("parent"),
},
functionPropMerge: "combine",
});
```

**Behavior**

Console logs:

```
child
parent
```

---

### 4. Declarative API with ``

```tsx
export function Layout({ children }: { children: React.ReactNode }) {
return (


{children}

{children}


);
}
```

```tsx
function PageContent() {
return (
<>

Welcome

Hello, world!

>
);
}


;
```

**Output**

```html


Welcome


Hello, world!



```

---

### 5. Fallback Slot Content

```tsx
Default Hero}>
{children}

```

If no `data-slot="hero"` is found, it renders:

```html

Default Hero

```

---

### 6. Slot Markers

**Marker Declaration**

```tsx
const HeroSlot = SlotUtils.createMarker("hero");

function Page() {
return (

Custom Hero


);
}
```

**Parent**

```tsx
Default Hero}>

```

---

### 7. Slot Validation

```tsx
SlotUtils.validate(children, ["header", "footer"]);
```

- Dev-only.
- Warns if `data-slot="header"` or `footer` is missing in children.

---

## Use Cases

- **Composable Layouts**: Dynamically slot content into shared layouts.
- **Design Systems**: Enable flexible API layers with predictable slot names.
- **Multi-brand / White-label UIs**: Inject branding-specific content without hardcoding.
- **Next.js Layouts**: Use context + slots to bridge `app/layout.tsx` and pages.
- **Dynamic Prop Injection**: Apply analytics, A/B testing, or class injection to specific slots.

---

## API Reference

### `getCmpByAttr

()`

```ts
function getCmpByAttr

(
options: ComponentFinderProps


): ReactNode | ReactNode[] | null;
```

### `ComponentFinderProps`

```ts
interface ComponentFinderProps

{
children: ReactNode;
attribute?: string;
value?: string;
props?: Partial

;
debug?: boolean;
findAll?: boolean;
onFound?: (component: ReactElement) => void;
functionPropMerge?: "combine" | "override";
}
```

### `Slot`

```tsx
}
>
{children}


```

### `SlotUtils`

```ts
SlotUtils.createMarker(name: string, attribute?: string): Component
SlotUtils.validate(children: ReactNode, requiredSlots: string[], attribute?: string): void
```

---

## Caveats

- **Next.js layouts** require a shared context if crossing page boundaries.
- `getCmpByAttr` only works on elements rendered within the same render cycle.
- This is **not a DOM query tool** – it’s entirely based on **React element trees**.

---

## License

MIT License