Screencasts
Home Tag Author Help

You've successfully subscribed to Codecourse Blog!

Subscribe to Codecourse Blog

Stay up to date! Get all the latest & greatest posts delivered straight to your inbox

Renderless Vue Components in 5 Minutes

Renderless components in Vue are components that tuck away functionality without dictating how the UI for that functionality should look.

Simply put, renderless components say "here's the functionality you need, it's up to you to use it and style it".

The main benefit is reusability of the core functionality. Imagine you wanted to 'toggle' something on and off. This could be in the form of a toggle button, or a simple text-based toggle.

With renderless components, you don't need two different components for this differing UI.

For the purpose of this article, let's roll with the example of this component. We'll build this with the standard Options API in just a moment.

But first.

A simple renderless component

Let's start with a really simple example.

export default {
    data () {
        return {
            greeting: 'Hello renderless component'
        }
    },

    render () {
        return this.$slots.default({
            greeting: this.greeting
        })
    }
}

Here's how we'd use it.

<Simple>
    <template v-slot="{ greeting }">
        {{ greeting }}
    </template>
</Simple>

The component uses a render method to pass down, into the default slot, the things we need. To access that stuff, we use a template element and v-slot to extract this.

That is essentially what a renderless component is. Rather than output the greeting in the component, we're exposing it, and leaving up to the implementation to decide how that data or functionality gets used.

A toggle renderless component

Here's the renderless component for the toggle functionality we saw above.

export default {
    props: {
        initialState: {
            type: Boolean,
            default: false
        }
    },

    data () {
        return {
            state: this.initialState
        }
    },

    render () {
        return this.$slots.default({
            state: this.state,
            toggle: this.toggle
        })
    },

    methods: {
        toggle () {
            this.state = !this.state
        }
    }
}

We have:

  1. An initialState that can be passed in to set the initial on/off state.
  2. The state being exposed, which is whether it's toggled or not.
  3. A toggle method, also being exposed, that toggles the state between on/off (true/false).

Here's how we'd use it.

<Toggle>
    <template v-slot="{ state, toggle }">
        {{ state === true ? 'On' : 'Off' }}
        <button v-on:click="toggle">Toggle</button>
    </template>
</Toggle>

If you wanted to set the initialState, that works just like a normal prop.

<Toggle :initialState="true">
    <template v-slot="{ state, toggle }">
        {{ state === true ? 'On' : 'Off' }}
        <button v-on:click="toggle">Toggle</button>
    </template>
</Toggle>

You're now free

With the functionality tucked away and exposed when this component is being used, you're free to implement it again and again with a different UI, as needed.

Here's an example using Tailwind CSS to style the toggle functionality using the same renderless component.

<Toggle>
    <template v-slot="{ state, toggle }">
        <button v-on:click="toggle" type="button" class="bg-gray-200 relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" role="switch" aria-checked="false" :class="{ 'bg-indigo-600': state, 'bg-gray-200': !state }">
            <span class="sr-only">Use setting</span>
            <span aria-hidden="true" class="translate-x-0 pointer-events-none inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200" :class="{ 'translate-x-5': state }"></span>
        </button>
    </template>
</Toggle>

Less confused?

If you were initially confused by the myriad examples around renderless components, I hope this has cleared up your confusion.

There is a lot more you can do with renderless components depending on the complexity of what you're building, but this wasn't intended to cover that – my goal was to explain as simply as possible what renderless components are.

I hope it's helped.