Subscribe on changes!

Allow components to define the slot they are always filling

avatar
Jan 7th 2023

What problem does this feature solve?

We were currently working on a new UI component library where we want to try and keep the work for devs using it as minimal as possible. Let's consider we have a Page component that comes with a Header and a Body. The Header consists of a Title, and an Icon, and we can have some action buttons.

Keeping our devs in mind, we want to allow them to be able to do the following:

<template>
  <Page>
    <PageHeader>
      <PageTitle>Some brilliant page</PageTitle>
      <PageIcon><SomeIconComponent></SomeIconComponent></PageIcon>
      <PageActions>
        <Button>Update</Button>
        <Button>Delete</Button>
      </PageActions>
    </PageHeader>
    <PageBody>
      ... Contents could go here
    </PageBody>
  </Page
</template>

Now obviously we don't want the devs to alwahs have to think about how to structure the child components or in what order to pass them in. Our UX designer might like the icon to the left and the buttons to the right of the screen, but he might change his mind later down the road... So: enter named slots.

Just looking at the PageHeader, we would create a component like this:

PageHeader.vue

<template>
  <div class="flex flex-row items-center justify-between">
    <div class="flex flex-row items-center gap-4>
      <slot name="icon"></slot>
      <slot name="title"></slot>
    </div>
    <slot name="actions"></slot>
  </div>
</template>

Now this all works brilliantly, but now to pass down the content to the right scope, a developer would have to do the following:

<PageHeader>
  <template v-slot:icon>
    <PageIcon>
      <SomeIconComponent></SomeIconComponent>
    </PageIcon>
  </template>
  <template v-slot:title>
    <PageTitle>
      Some brilliant page
    </PageTitle>
  </template>
  <template v-slot:actions>
    <PageActions>
      <Button>Update</Button>
      <Button>Delete</Button>
    </PageActions>
  </template>
</PageHeader>  

This gets a bit verbose to me, so I was looking at the short hands that are supported When there is only one slot passed down, we can get rid of the template tags because

<SomeComponent v-slot:name>
  Hello
</someComponent>

is a shorthand for

<SomeComponent>
  <template v-slot:name>
    Hello
  </template>
</SomeComponent>

But I can't get around the idea that this shorthand is a little useless. Most of the times, you would be naming your slots exactly because you have more than one in a parent component, so those are the use cases where the shorthand doesn't work.

in an ideal world, I want my devs to not having to worry about this, and just be able to nest the components and have the components do their work

What does the proposed API look like?

So building on the example above, we would really like it if we could build our components as follows:

Page.vue

<template>
  <div>
    <slot name="header"></slot>
    <slot name="body"></slot>
  </div>
</template>

PageHeader.vue

<template v-slot:header> <!-- we're saying that this component will ALWAYS be providing content for a slot named header -->
  <div>
    <slot></slot>
  </div>
</template>

PageBody.vue

<template v-slot:body> <!-- we're saying that this component will ALWAYS be providing content for a slot named body -->
  <div>
    <slot></slot>
  </div>
</template>

then when we are creating a page, we could just do this:

<Page>
  <PageHeader>
    header content goes here
  </PageHeader>
  <PageBody>
    body goes here
  </PageBody>
</Page>

and vue would know that the pageheader and pagebody components would need to go into the slots named header and body respectively as defined in the Page component.

I've been trying to bend my mind as to why the shorthand doesn't work like this to begin with, but I'm sure I'm missing some info there, so feel free to let me know.

But when creating UI components like this, I believe somehing like this would really make things easier, and clean up a lot of the code we sometimes have.

avatar
Jan 8th 2023
<Page>
  <PageHeader>
    header content goes here
  </PageHeader>
  <PageBody>
    body goes here
  </PageBody>
</Page>

The above code means the same as the following code in terms of the current semantics:

<Page>
  <template v-slot:default>
    <PageHeader>
      header content goes here
    </PageHeader>
    <PageBody>
      body goes here
    </PageBody>
  </template>
</Page>

So I think it's probably the biggest obstacle to supporting this feature.

avatar
Jan 8th 2023

Yes, I realise this. However, since a component in itself is a template, it feels like we have nested