nano-slots
Version:
A super lightweight modern alternative to [`react-slot-fill`](https://github.com/camwest/react-slot-fill) with familiar API.
176 lines (125 loc) • 4.14 kB
Markdown
# nano-slots
A super lightweight modern alternative to [`react-slot-fill`](https://github.com/camwest/react-slot-fill) with familiar API.
- [x] Control sub-components rendering with `Slot` and `Fill`
- [x] Render content of sub-component in multiple places
- [x] Speedy - `Fill` and `Slot` communicate directly with each other
- [x] Tested with [`testing-library`](https://testing-library.com)
- [x] Written in TypeScript
- [x] Zero dependencies
- [x] Only ~431 B
## 📦 Install
```sh
npm i -S nano-slots
```
```sh
yarn add nano-slots
```
## 💻 Usage
### Create a component and define slots
```js
import { Box, Flex } from 'theme-ui'
import { SlotsProvider, Slot } from 'nano-slots'
export const MediaObject = ({ children }) => (
<SlotsProvider>
<Flex>
<Box mr={3}>
<Slot name="media-side" />
</Box>
<Box>
<Box mb={2}>
<Slot name="media-title" />
</Box>
<Box>
<Slot name="media-description" />
</Box>
{children}
</Box>
</Flex>
</SlotsProvider>
)
```
### Render elements directly inside each slot
```js
import { Fill } from 'nano-slots'
import { MediaObject } from './media-object'
const MyApp = () => (
<MediaObject>
<Fill name="media-side">
<img src='https://placekitten.com/200' alt="Kitten" />
</Fill>
<Fill name="media-title">
<h3>Mew</h3>
</Fill>
<Fill name="media-description">
<p>Purr purr purr</p>
</Fill>
</MediaObject>
)
```
[](https://codesandbox.io/s/nano-slots-s0y0t?fontsize=14&hidenavigation=1&theme=dark)
## 📖 API
### `SlotsProvider`
```js
import { SlotsProvider } from 'nano-slots'
```
#### Props
- `children: ReactNode` — any valid react children element
#### Description
Creates a context for `Slot` / `Fill` components.
### `Slot`
```js
import { Slot } from 'nano-slots'
```
#### Props
- `name: string` — unique slot name for current `SlotsProvider`
- `children?: ReactNode` — fallback in case `Fill` with matching `name` not provided, optional
- `onChange?(hasFilled: boolean): void` — callback for detecting state changes, on `true` children of matching `Fill` is rendered and fallback is hidden
#### Description
Define the target slot for `Fill` component. Can be used multiple times with the same name inside each `SlotsProvider`.
### `Fill`
```js
import { Fill } from 'nano-slots'
```
#### Props
- `name: string` — unique slot name for current `SlotsProvider`
- `children: ReactNode` — will be rendered inside matching `Slot`
#### Description
Render children into matching `Slot` of current `SlotsProvider`.
### `createSlots`
```js
import createSlots from 'nano-slots'
```
#### Description
Designed for more advanced usages and stronger types.
Returns an object containing:
- `.Provider` — same as [`SlotsProvider`](#SlotsProvider), but with different context
- `.Slot` — same as [`Slot`](#Slot), but with own context
- `.Fill` — same as [`Fill`](#Fill), but with own context
Returned `Slot` and `Fill` can be used without a `Provider`.
### Types
```ts
export interface ProviderProps {
children: React.ReactNode;
}
export function SlotsProvider(props: ProviderProps): JSX.Element;
export interface SlotProps<Names extends PropertyKey> {
name: Names;
children?: React.ReactNode;
}
export function Slot(props: SlotProps): JSX.Element;
export interface FillProps<Names extends PropertyKey> {
name: Names;
children?: React.ReactNode;
}
export function Fill(props: FillProps): null;
export default function createSlots<Names extends PropertyKey>(): {
Provider: (props: SlotsProviderProps): JSX.Element;
Slot: (props: SlotProps<Names>): JSX.Element;
Fill: (props: FillProps<Names>): null;
}
```
## Alternatives
- [`react-slot-fill`](https://github.com/camwest/react-slot-fill) - abandoned project that inspired this one
- [`react-view-slot`](https://github.com/robik/react-view-slot) - more modern approach, but [12x times bigger](https://bundlephobia.com/result?p=react-view-slot@1.0.1)
---
MIT © John Grishin