Skip to content

cpud36/solid-slots

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Solid Slots

A cursed way to have vue-like slots api in SolidJS.

This library does not use Portals, and provides a ssr-friendly slots api.

What it is

When designing compound components our api is sometimes at odds with underlying layout. Consider a layout component, where we want to put the header and the content into different elements. E.g. we want html like this:

<div>
    <h1>Title</h1>
    <main>
        <p>Info</p>
        <p>Extra info</p>
    </main>
</div>

This library allows us to provide an api like this:

<Layout>
    <Header>Title</Header>
    <p>Info</p>
    <p>Extra info</p>
</Layout>

Here, we extract our title from our children, and put it outside of <main> tag.

This is done via core primitive resolveSlots:

const Header = createSlot();
function Layout(props) {
    const [header, children] = resolveSlots(() => props.children, Header);
    return <div>
        <h1>{header()}</h1>
        <main>{children()}</main>
    </div>;
}

And that's it.

Stability

This library abuses some implementation details of SolidJS rendering to implement this nice api. These implementation details are unlikely to change, but beware.

The api of the library has settled general direction, but is expected to change in minor details. There are some non-trivial uses that are possible, but not yet supported.

How to

The core idea of the library is slots - it is the component that can be introspected. They are created via createSlot function:

const Header = createSlot();
const Footer = createSlot();
const Aside = createSlot();

The returned slots are solid components, that just render their children:

<Header>
    <b>Hello</b> World
</Header>

Renders as just <b>Hello</b> World. But they have a superpower to be introspected.

In your consuming component, you call resolveSlots, to extract slotted components from your slots.

function Layout(props) {
    const [header, footer, children] = resolveSlots(() => props.children, Header, Footer);
    // ...
}

Here resolveSlots traverses the props.children. It collects all children that are Header into header() accessor, and all children that are Footer into footer() accessor. All other children are put into the children() accessor.

Note, how we ignore <Aside> slot. If the user passes an unknown slot, resolveSlots puts it into children.

Limitations

The question is, when will you see a slot in you children. It is a non-trivial question, and I will not give a definitive answer in all cases.

Here is a common list of examples:

const MyHeader = (props) => <Header>My {props.children}</Header>;

<Layout>
    <Header>Yes</Header>
    <MyHeader>Yes</MyHeader>
    <><Header>Yes</Header></>
    <div><Header>No</Header></div>
    <Show when={cond}>
        <Header>Probably</Header>
    </Show>
    <For each={[1, 2, 3]}>{() => <>
        <Header>Probably</Header>
    </>}</For>
</Layout>

On top of that, any call to children helper from solid-js, or to resolveSlots expands all slots. E.g.

import {children} from "solid-js";

const resolved = children(() => props.children);
// Slot will never be seen, as `children` has removed all slots machinery
const [slot, rest] = resolveSlots(resolved, Slot);

About

Slots api for solid

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published