1.0.0 • Published 6 months ago

@isilin/react-slot v1.0.0

Weekly downloads
-
License
MIT
Repository
github
Last release
6 months ago

📦 React Slot Components

Storybook Build Status License: MIT

Create reusable React components with simple, typed, and powerful slots.


🗺️ Table of Contents


🧪 Try the Project

🛠️ Prerequisites

  • Node.js (recommended version: >=18.x)
  • Yarn or npm

📚 Install and Run Storybook

npm install
npm run dev
# ou
yarn install
yarn dev

This will open Storybook at http://localhost:6006.

🧩 The "Slot" Pattern in React

The slot pattern is an approach inspired by advanced libraries like Radix UI and Headless UI. It allows dynamic content injection into a component while preserving structure and encapsulated logic.

📘 Why Use Slots?

  • Build a clear and declarative API
  • Offer complete flexibility to component users
  • Add or remove optional parts without breaking the component
  • Separate structure from content
  • Create reusable and composable components
  • Provide explicit injection points for content

📦 Example: Card Component with Slots

// Card.tsx
import { PropsWithChildren } from 'react';
import { defineSlotComponent, getSlots } from '../../lib';

import classNames from 'classnames/bind';
import styles from './Card.module.css';
const cx = classNames.bind(styles);

export const Card = defineSlotComponent(
  ({ children }: PropsWithChildren) => {
    const { body, footer, header } = getSlots(children, Card);

    return (
      <div className={cx('card')}>
        {header && <div className={cx('card-header')}>{header}</div>}
        {header && (body || footer) && <Card.Separator />}
        {body && <div className={cx('card-body')}>{body}</div>}
        {body && footer && <Card.Separator />}
        {footer && <div className={cx('card-footer')}>{footer}</div>}
      </div>
    );
  },
  {
    slots: {
      header: ({ children }: PropsWithChildren) => <>{children}</>,
      body: ({ children }: PropsWithChildren) => <>{children}</>,
      footer: ({ children }: PropsWithChildren) => <>{children}</>,
    },
    extras: {
      Separator: () => <span className={cx('separator')} />,
    },
  },
);

🧪 Usage

// App.tsx
export const App = () => {
  return (
    <Card>
      <Card.Header>Ma jolie carte</Card.Header>
      <Card.Body>
        Ceci est le contenu principal de la carte. Tu peux y mettre n'importe
        quoi : du texte, des images, ou même d'autres composants.
      </Card.Body>
      <Card.Footer>Dernière mise à jour : aujourd’hui.</Card.Footer>
    </Card>
  );
};

Under the hood, each slot (Header, Footer, Content, etc.) is automatically detected and placed in the right structure.

📚 Additional Resources

Feel free to explore the component live in Storybook to better understand how the slots are used!

☕ Support this Project

If you like this project, you can support me on Ko-fi ❤️

Soutenir sur Ko-fi

🪪 Licence

This project is licensed under the MIT License.

🤝 Contributing

Contributions are welcome! Feel free to open an issue or submit a pull request.


Made with Love by Isilin