Marco
Marco

Reputation: 1620

Pass ref to default slot in renderless component

I am trying to build a renderless component in vue 3 and want to pass a ref to the default slot.

When I am using the h render function I can just pass a ref:

return h('div', {ref: someRef}); // works

If I try to do the same with the default slot, it does not work:

return slots.default({ ref: someRef}) // does not work
return slots.default({ someRef}) // also does not work

Is there any way to do this without wrapping the default slot into another div or similar?

Checked already the documentation and other resources, but couldn't find any solution.

Upvotes: 2

Views: 1291

Answers (1)

renatodeleao
renatodeleao

Reputation: 92

Direct answer

Yes return a function from your setup hook!

setup(_, slots) { 
  const someRef = ref()

  return () => slots.default({ ref: someRef })
}
  1. vue3 docs link
  2. vue3 docs for renderless component pattern

Contextual answer for those in the comment section questioning the renderless/headless pattern

Yes, sometimes you just want a renderless (or headless as the kids these days say) wrapper for functionality (logic), without limiting the HTML that consumers can use.

But for that functionality to work, the renderless/headless component still needs to identify some of the HTML that consumers render into the default slot, in order to provide DOM-related functionality for example.

I have the same struggle when using this pattern, and have been relying on a "hack": passing specific data attributes via slot scope, that consumers need to bind to the desired elements (not only root) and then using good old document.querySelector to grab them

I has served me well in vue2 and I've been using it in production with no problems, but I was wondering if with the new dynamic :ref attribute of vue3, we can do it differently: probably passing a reactive ref variable and then doing a simple assign, and apparently it works.

<renderless #default="{ someRef }">
   <some-consumer-comp :ref="(el) => someRef.value = el" />
</renderless>
  1. Here's a sandbox demo old way for vue 2
  2. Here's a sandbox demo new way for vue 3

Do note that if you want to handle edge cases, like handling conditional rendering of slot content, you probably need to do a bit more work and add some watchers.

This pattern is a bit out of fashion these days, with the introduction of vue composables since you can abstract the logic into a useSomeFunctionality and use it directly on the component you want, but it's sill a valid one IMO.

Cheers!

Upvotes: 1

Related Questions