0
0
Vueframework~20 mins

Compound components pattern in Vue - Practice Problems & Coding Challenges

Choose your learning style9 modes available
Challenge - 5 Problems
🎖️
Compound Components Master
Get all challenges correct to earn this badge!
Test your skills under time pressure!
component_behavior
intermediate
2:00remaining
What will be rendered by this compound component?

Consider a Vue compound component where Modal provides context and ModalHeader and ModalBody consume it. What will be the visible output?

Vue
<template>
  <Modal>
    <ModalHeader>Welcome</ModalHeader>
    <ModalBody>This is the body content.</ModalBody>
  </Modal>
</template>

<script setup>
import { provide, inject, ref } from 'vue'

const Modal = {
  setup(_, { slots }) {
    const title = ref('Default Title')
    provide('modalTitle', title)
    return () => (
      <section aria-modal="true" role="dialog">
        {slots.default()}
      </section>
    )
  }
}

const ModalHeader = {
  setup(_, { slots }) {
    const title = inject('modalTitle')
    return () => (
      <header><h1>{slots.default ? slots.default() : title.value}</h1></header>
    )
  }
}

const ModalBody = {
  setup(_, { slots }) {
    return () => <main>{slots.default()}</main>
  }
}
</script>
A<section><main>This is the body content.</main></section>
B<section><header><h1>Default Title</h1></header><main>This is the body content.</main></section>
C<section><header><h1></h1></header><main>This is the body content.</main></section>
D<section><header><h1>Welcome</h1></header><main>This is the body content.</main></section>
Attempts:
2 left
💡 Hint

Look at how ModalHeader uses the slot content versus the injected title.

state_output
intermediate
2:00remaining
What is the value of the shared state after clicking the button?

In this compound component pattern, a shared state is provided by the parent and updated by a child button. What will be the final value of count after clicking the button once?

Vue
<template>
  <Counter>
    <CounterDisplay />
    <CounterButton />
  </Counter>
</template>

<script setup>
import { ref, provide, inject } from 'vue'

const Counter = {
  setup(_, { slots }) {
    const count = ref(0)
    provide('count', count)
    provide('increment', () => { count.value++ })
    return () => slots.default()
  }
}

const CounterDisplay = {
  setup() {
    const count = inject('count')
    return () => <p>Count: {count.value}</p>
  }
}

const CounterButton = {
  setup() {
    const increment = inject('increment')
    return () => <button onClick={increment}>Add</button>
  }
}
</script>
ACount: 0
BCount: undefined
CCount: 1
DCount: NaN
Attempts:
2 left
💡 Hint

Think about how the increment function changes the shared count state.

📝 Syntax
advanced
2:00remaining
Which option correctly defines a compound component with slots and provide/inject?

Which code snippet correctly implements a compound component pattern in Vue 3 using provide and inject with slots?

A
const Parent = { setup(_, { slots }) { const data = ref('info'); provide('data', data); return () =&gt; &lt;div&gt;{slots.default()}&lt;/div&gt; } };
const Child = { setup() { const data = inject('data'); return () =&gt; &lt;p&gt;{data.value}&lt;/p&gt; } };
B
const Parent = { setup() { const data = ref('info'); provide('data', data); return () =&gt; &lt;div&gt;&lt;slot /&gt;&lt;/div&gt; } };
const Child = { setup() { const data = inject('data'); return () =&gt; &lt;p&gt;{data}&lt;/p&gt; } };
C
const Parent = { setup(_, { slots }) { const data = ref('info'); provide('data', data); return () =&gt; &lt;div&gt;{slots.default}&lt;/div&gt; } };
const Child = { setup() { const data = inject('data'); return () =&gt; &lt;p&gt;{data.value}&lt;/p&gt; } };
D
const Parent = { setup(_, { slots }) { const data = ref('info'); provide('data', data); return () =&gt; &lt;div&gt;{slots.default()}&lt;/div&gt; } };
const Child = { setup() { const data = inject('data'); return () =&gt; &lt;p&gt;{data}&lt;/p&gt; } };
Attempts:
2 left
💡 Hint

Remember that slots.default is a function and inject returns a ref if provided a ref.

🔧 Debug
advanced
2:00remaining
Why does this compound component fail to update the shared state?

Given this compound component, why does clicking the button not update the displayed count?

Vue
<template>
  <Counter>
    <CounterDisplay />
    <CounterButton />
  </Counter>
</template>

<script setup>
import { ref, provide, inject } from 'vue'

const Counter = {
  setup(_, { slots }) {
    const count = ref(0)
    provide('count', count)
    provide('increment', () => { count.value++ })
    return () => slots.default()
  }
}

const CounterDisplay = {
  setup() {
    const count = inject('count')
    return () => <p>Count: {count.value}</p>
  }
}

const CounterButton = {
  setup() {
    const increment = inject('increment')
    return () => <button onClick={increment}>Add</button>
  }
}
</script>
ABecause <code>CounterDisplay</code> renders <code>{count}</code> instead of <code>{count.value}</code>, so it shows the ref object, not the number.
BBecause <code>increment</code> is not called on button click due to missing parentheses.
CBecause <code>count</code> is not provided properly in <code>Counter</code> component.
DBecause <code>slots.default()</code> is not called in <code>Counter</code> component.
Attempts:
2 left
💡 Hint

Check how the reactive ref is accessed in the display component.

🧠 Conceptual
expert
2:00remaining
What is the main advantage of using the compound components pattern in Vue?

Why would a developer choose to use the compound components pattern instead of passing props directly to child components?

AIt eliminates the need for slots, simplifying component templates.
BIt allows sharing state and behavior implicitly via provide/inject, enabling flexible composition without prop drilling.
CIt forces all child components to receive props explicitly, improving type safety and clarity.
DIt automatically optimizes rendering performance by memoizing all child components.
Attempts:
2 left
💡 Hint

Think about how state and functions are shared between parent and children without passing props down many levels.