Lesson 5 of 7

Composables

You will learn how to write composables: plain that use the Composition to share reactive logic between components. They are Vue's equivalent of React's custom hooks.

// composables/useFetch.js
import { ref, watchEffect, toValue } from 'vue'

export function useFetch(url) {
  const data    = ref(null)
  const error   = ref(null)
  const loading = ref(false)

  watchEffect(async () => {
    const resolvedUrl = toValue(url)   // works with ref or plain string
    if (!resolvedUrl) return

    data.value    = null
    error.value   = null
    loading.value = true

    try {
      const res = await fetch(resolvedUrl)
      if (!res.ok) throw new Error(`HTTP ${res.status}`)
      data.value = await res.json()
    } catch (e) {
      error.value = e
    } finally {
      loading.value = false
    }
  })

  return { data, error, loading }
}
JavaScript sandboxCtrl+Enter runs
<!-- Using the composable -->
<template>
  <div>
    <div v-if="loading">Loading...</div>
    <div v-else-if="error">Error: {{ error.message }}</div>
    <ul v-else>
      <li v-for="post in data" :key="post.id">{{ post.title }}</li>
    </ul>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { useFetch } from './composables/useFetch'

// Works with a static URL
const { data, loading, error } = useFetch('https://jsonplaceholder.typicode.com/posts')

// Also works reactively — refetches when postId changes!
const postId = ref(1)
const { data: post } = useFetch(() => `https://jsonplaceholder.typicode.com/posts/${postId.value}`)
</script>

<!-- composables/useLocalStorage.js -->
<script>
import { ref, watch } from 'vue'

export function useLocalStorage(key, defaultValue) {
  const stored = localStorage.getItem(key)
  const value  = ref(stored ? JSON.parse(stored) : defaultValue)

  watch(value, (newVal) => {
    localStorage.setItem(key, JSON.stringify(newVal))
  }, { deep: true })

  return value
}
</script>

<!-- Using useLocalStorage -->
<script setup>
import { useLocalStorage } from './composables/useLocalStorage'

const theme    = useLocalStorage('theme', 'light')
const settings = useLocalStorage('settings', { notifications: true, lang: 'en' })
</script>
Sandboxed page
Finished reading?Mark it done to track your progress.

Tip: use the ← and → keys to move between lessons.