A Component with a Slot

The café's page has several boxes that look the same: a white box with a border and a heading, and in it, something different each time. The opening hours are a paragraph, the menu is a list, and the order is a list with a button. One component for the box would be handy, but props don't fit here. A prop carries a value, such as a text or an object, and this box has to carry a piece of the page.

For that, a component has a slot: a place in its template that its parent fills. Here's Panel.vue, a box with a heading from its prop title and a slot under the heading:

<script setup>
defineProps({
    title: { type: String, required: true },
});
</script>

<template>
    <section class="panel">
        <h2>{{ title }}</h2>
        <slot />
    </section>
</template>

The parent writes the component with an opening and a closing tag, and puts the content between them, as it would with an HTML element:

<Panel title="Opening hours">
    <p>Every day from 7 am to 7 pm.</p>
</Panel>

Vue shows the panel's template, with the content in place of <slot />. The page gets:

<section class="panel">
    <h2>Opening hours</h2>
    <p>Every day from 7 am to 7 pm.</p>
</section>

The content can be anything you'd write in a template: text, several elements, a list with v-for, even other components. And each time you use the panel, you can give it different content.

Until now, your components had nothing between their tags, so you wrote them self-closing, as <MenuItem />. And a component without <slot /> does nothing with the content between its tags: Vue leaves it out, without a warning. When content doesn't show up, check first that the component has a slot.

Instructions

  1. In Panel.vue, put <slot /> under the heading, so that the panel with the opening hours shows its paragraph.

  2. In App.vue, add a second panel under the first one, with the title Today's cake and the paragraph Carrot Cake with walnuts, €4.