Anatomy of a Vue composable file.

Composables are well-documented, but some features might remain a mystery, such as the ability to have shared state across components. Personally I usually use composables as files and make use of shared state that often replace the need for a state manages like Pinia/Vuex.

In this example we have an app that has some e-commerce features like browsing the product catalog by category. All products are loaded in the background in a shared state and the component instances have a filtered list of products by categoryId.

// useProducts.js
import { ref, computed } from 'vue'
import axios from 'axios'

// Products variable is a state that is shared over different instances!
const products = ref([])
// A function to populate the products data. 
// We can also add conditions where this is done only once!
const loadProducts = async () => {
    const { rows } = await axios.get('/products')

    products.value = rows
}

/**
 * @param {Object} settings instance specific settings for initialization
 * @param {Number} settings.categoryId a categoryId is provided in this example
 * @returns {Object}
 */
export default function useProducts({ categoryId }) {
    // Rows is a computed value that exists in this instance.
    const rows = computed(() => {
        return products.value.filter(row => row.categoryId === categoryId)
    })

    // Run some data initializing code when this composable is being used.
    // You can also use some Vue hooks like onMounted here.
    loadProducts()

    return {
        rows,
    }
}

And to use this in a component simply import the file and use the setup function. The categoryId here could originate from component properties or from the route parameters.

// ProductList.vue
import useProducts from 'useProducts.js'

export default {
    setup() {
        const { rows: products } = useProducts({ categoryId: 7 })

        return {
            products,
        }
    }
}

And that's it! This simple paradigm is very extendable and re-usable. The key points here are: shared state vs instance specific state or computed values and running initializing code.

Have something to say on this topic? Here's my Twitter: @opiispanen