Vue.js - Evolution From Renderless Component to Composable Function

  • #Vue.js
  • #Renderless Component
  • #Composable function
  • #Composition API
  • #Vue 3
  • #Reutilizability
June 4, 2023|9 min read

Introduction

This article delves into the concept of renderless components and provides a comprehensive guide on building them in Vue.js. It not only explores the benefits of using renderless components but also demonstrates how they enable the creation of highly customizable components that can be effectively employed in various instances throughout an application. Additionally, the article showcases the process of converting a renderless component into a composable function using the Composition API, highlighting how this approach can significantly simplify the component's complexity.

What is a renderless component?

A renderless component is an essential element in web development that functions without rendering specific HTML elements. Its main purpose is to provide logic and data exclusively to the components that utilize it. Essentially, it acts as a component that accepts props and events and exposes slots to the parent component. This unique characteristic enables developers to create highly customizable components that can be employed in diverse ways, depending on the specific requirements of the application.

Why use a renderless component?

Renderless components offer valuable advantages in web development, serving as a means to create reusable components that can be utilized in multiple instances across an application. Moreover, they prove beneficial in constructing highly customizable components that can adapt to various scenarios and requirements. To illustrate, a renderless component can be employed to fashion an input field that facilitates the creation of custom validation rendering for the field by exposing the necessary slot props to the parent component. for more information about this example you can read this article Renderless components pattern in Vue 3.

How to build a renderless component in Vue.js

To illustrate the process of building a renderless component in Vue.js, we will create an Autocomplete component that can be used to search for a specific item in a list of items. The component will accept a list of items, a search query, and a callback function as props. It will also expose a slot that will be used to render the list of items. The component will then filter the list of items based on the search query and pass the filtered list to the slot. The callback function will be used to pass the selected item to the parent component.

The Autocomplete component provides a fundamental implementation where it exposes a limited set of properties and functions. This allows developers to have control over the UI and customize it according to their desired visual appearance and behavior.

Basic example

AutoComplete.tsts
const AutoComplete = defineComponent({
  props: {
    items: {
      type: Array,
      required: true,
    },
    searchFn: {
      type: Function,
      required: true,
    },
  },
  emits: ['select'],
  setup(props, { emit, slots }) {
    const searchQuery = ref('') // Represents the search query entered by the user
    const results = ref([]) // Stores the search results based on the search query
    const onSearch = () => {
      // Implement the search functionality
    }
    const onSearchQueryChange = (val) => {
      // Handle the search query change
    }
    const select = (result) => {
      // Handle the selection of a result
    }
    return () => slots.default?.({
        searchQuery: searchQuery.value,
        results: results.value,
        onSearchQueryChange
      })
  },
})

Here we're going to use the component to render an input that provides the search query then render the filtered items as unordered list.

Search in the following items :

[ "Vue.js", "React", "Angular", "Svelte", "Ember", "Meteor" ]

Advanced example

Suppose we have a list of frameworks along with their corresponding logos, and our goal is to display them as a grid of cards. As the items in the list are objects, we'll need to provide a custom search function to the AutoComplete component that searches based on the framework's name.

Search for frontend framework :

So far, we've created a basic renderless component that can be used to render a list of items based on a search query. However, we can further enhance the component by adding more features to it. For example, we can add the ability to select an item from the list and emit an event to the parent component. We can also add the ability to clear the search query and reset the list of results.

Now, we will implement the same concept using a composable function. Since the renderless component focuses solely on handling the logic without rendering any UI, it is well-suited to be implemented as a composable function.

What's a composable function?

A composable function is a function that encapsulates a stateful logic using the reactivity API, lifecycle hooks, and dependency injection. It allows this logic to be reused across multiple components, enhancing code reusability and modularity.

Basic example

Let's start by creating a composable function that handles the logic of searching for a specific item in a list of items. The composable function will accept a list of items and a search query as arguments. It will then filter the list of items based on the search query and return the filtered list.

useAutocomplete.tsts
import { computed, ref } from 'vue'
export default function useAutocomplete<T = string>(items: T[], searchFn?: (query: string) => T[]) {
    const searchQuery = ref('') // Represents the search query entered by the user
    const results = computed(() => {
        // Stores the search results based on the search query
        return searchFn
            ? searchFn(searchQuery.value)
            : (items as string[]).filter((item) => searchQuery.value && item.includes(searchQuery.value))
    })
    return {
        searchQuery,
        results,
    }
}

Now, we can use the composable function to render an input that provides the search query then render the filtered items as unordered list.

Search in the following items :

[ "Vue.js", "React", "Angular", "Svelte", "Ember", "Meteor" ]

The composable approach has allowed us to reduce the amount of code needed to implement the same functionality.

Advanced example

Let's now create a composable function that takes a search function as parameter and render the results as grid of cards. The search function will be used to filter the list of items based on the search query.

Search for frontend framework :

Conclusion

Throughout this article, we have gained insights into the process of constructing a renderless component and a composable function. Additionally, we have explored their practical applications and witnessed how both approaches can be employed to achieve identical functionality. Notably, we have observed the advantageous nature of the composable approach, which enables us to significantly reduce the code required for implementing the desired functionality.