react-templated

Yet another implementation of slot components for React


Keywords
react, template, slot, slot-components
License
MIT
Install
npm install react-templated@1.0.1

Documentation

ci codecov downloads node npm MIT npm bundle size Conventional Commits

react-templated

Yet another implementation of slot components for React. It's inspired by WebComponents slots.

full documentation can be found on https://andres-kovalev.github.io/react-templated/

Description

react-templated provides several components to created templated components.

Installation

As any other npm package react-templated can be added to your project by following command:

npm i -S react-templated

It requires any version of react with new context API support as peer dependency, so it should be installed as well.

npm i -S react

Quick start

By default content for React component can be accessed by children prop:

function Parent() {
    return (
        <Child>
            <div>our div 1</div>
            <div>our div 2</div>
        </Child>
    );
}

...

function Child(props) {
    // here are our divs
    const { children } = props;

    ...
}

But some times we need to distribute children between different areas of our component. We might want to create a template. We can extract try to extract separate children and use those:

function Child({ children }) {
    const [ child1, child2 ] = children.props.children;

    return (
        <React.Fragment>
            <div className="header">
                {child1}
            </div>
            <div className="body">
                {child2}
            </div>
        </React.Fragment>
    );
}

Looks a bit ugly. Also, we need to provide some safe-checks/fallbacks to prevent issues when user provides less than 2 children and we need to find some way to be able to provide only 2nd/3rd/etc item.

import _ from 'lodash';

function Child({ children }) {
    const [ child1, child2 ] = _.get(children, 'props.children', []);

    ...
}

function Parent() {
    return (
        <React.Fragment />
        <div>body</div>
    );
}

Another way is to use different props for different areas:

const Page = ({ header, body, footer }) => (
    <div className="page">
        <div className="header">
            { header }
        </div>
        <div className="body">
            { body }
        </div>
        <div className="header">
            { footer }
        </div>
    </div>
)

Looks much better, but it's still not so convenient to use such components and provide default content:

const App = () => (
    <Page
        header={ <Header /> }
        body={ <Body /> }
        footer={ <Footer /> }
        />
);

// or

const App = () => {
    const header = <Header />;
    const body = <Body />;
    const footer = <Footer />;

    const props = { header, body, footer };

    return <Page { ...props } />;
}

One more option is to use slots from react-templated package. First we need to create our template:

import { Slot, withSlots, Value } from 'react-templated';

// it's not necessary to provide any props for template
const PageTemplate = () => (
    <div className="page">
        <div className="header">
            <Slot name="header">
                Slot content will be found by slot name
                We can provide default content for the slot here
            </Slot>
        </div>
        <div className="body">
            <Slot>
                We can skip name to mark slot as default
            </Slot>
        </div>
        <div className="header">
            <Slot name="footer" />
            {/* it's not necessary to provide default content */}
        </div>
    </div>
);

The next step is to enhance our component with slot power:

const Page = withSlots(PageTemplate);

Now we can provide any content for Page component and it will be distributed automatically:

const App = () => (
    <Page>
        <Value name="header">
            Header content
        </Value>
        <Value name="footer">
            Footer content
        </Value>
        Everything else will be considered as default content
        and will appear in default slot
    </Page>
);

That's it. For those, who don't like HoCs react-templated provides Templated wrapper:

import { Templated } from 'react-templated';

const Page = ({ children }) => (
    <Templated content={ children }>
        <PageTemplate />
    <Templated>
);

The code abowe is equivalent to withSlots() HoC.