Composables are one of the most exciting features of the Composition API in Vue 3. A composable in the Composition API is essentially an enhanced version of a mixin in Options API. I’d earlier written on the advantages of Composition API over Options API and there I mentioned how useful composables are.
We can create a composable and use it in whichever component, however, we want. It greatly increases code reusability for common functionalities.
Let’s have a look at an example where we create a composable to control the font size of any element in any component. We create a composable with the convention use/use<ComposableName> where use/ is the directory we put all the composables in. So to create a composable that controls font size, we could name it use/useFonts.
So, here’s our use/useFonts.js file having a composable:
import { reactive, computed } from "vue"
export function useFonts() {
const fontData = reactive({
fontSize: 16
})
const isFontLarge = computed(() => {
if (fontData.fontSize > 30) return true
return false
})
const increaseFontSize = (amount) => {
fontData.fontSize += amount
}
const decreaseFontSize = (amount) => {
fontData.fontSize -= amount
}
const resetFontSize = () => {
fontData.fontSize = 16
}
return {
fontData,
isFontLarge,
increaseFontSize,
decreaseFontSize,
resetFontSize
}
}
Code language: JavaScript (javascript)
Here we are using a reactive state named fontData with a fontSize in it. We’re then controlling the values of the state with the methods increaseFontSize, decreaseFontSize and resetFontSize.
We can now this composable in whichever component we want. Here’s a component using it:
<script setup>
import { useFonts } from '../use/useFonts';
const {
fontData,
isFontLarge,
increaseFontSize,
decreaseFontSize,
resetFontSize } = useFonts()
</script>
<template>
<div>
{{ fontData.fontSize }}
<button @click="increaseFontSize(4)">Increase font size</button>
<button @click="decreaseFontSize(4)">Decrease font size</button>
<button @click="resetFontSize()">Reset font size</button>
<div>
Is font large? {{ isFontLarge ? 'Yes ✅' : 'No ❌' }}
</div>
<h1 :style="{ fontSize: `${fontData.fontSize}px` }">My amazing heading</h1>
</div>
</template>
<style>
h1 {
font-family: Impact, Haettenschweiler, 'Arial Narrow Bold', sans-serif
}
</style>
Code language: HTML, XML (xml)
You’d notice that in the above file, we’ve first imported the composable as useFonts on line 2. We’ve then destructured the useFonts so that we can access the reactive state and the methods.
If we want, we can also call the methods without destructuring. But the downside is that, we won’t be able to see at once what all methods we have available to use.
Here’s how a non-destructured version would look like:
<script setup>
import { useFonts } from '../use/useFonts';
const fonts = useFonts()
</script>
<template>
<div>
{{ fonts.fontData.fontSize }}
<button @click="fonts.increaseFontSize(4)">Increase font size</button>
<button @click="fonts.decreaseFontSize(4)">Decrease font size</button>
<button @click="fonts.resetFontSize()">Reset font size</button>
<div>
Is font large? {{ fonts.isFontLarge ? 'Yes ✅' : 'No ❌' }}
</div>
<h1 :style="{ fontSize: `${fonts.fontData.fontSize}px` }">
My amazing heading
</h1>
</div>
</template>
Code language: HTML, XML (xml)
Here’s what the working version of the above code would look like: