[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"header-categories":3,"post-complete-guide-vue3-composables":73,"parsed-post-50add5fc-4026-4267-b917-7f71ea9e35b0":148},{"success":4,"data":5},true,{"items":6,"pagination":70},[7,16,24,32,40,48,56,63],{"id":8,"name":9,"description":10,"parentId":11,"createdAt":12,"updatedAt":12,"parent":11,"children":13,"_count":14},"ai-future","AI & The Future","Artificial intelligence and emerging technology",null,"2026-07-24T06:02:45.990Z",[],{"posts":15},1,{"id":17,"name":18,"description":19,"parentId":11,"createdAt":20,"updatedAt":20,"parent":11,"children":21,"_count":22},"tools-productivity","Tools & Productivity","Developer tools and workflow optimization","2026-07-24T06:02:45.712Z",[],{"posts":23},7,{"id":25,"name":26,"description":27,"parentId":11,"createdAt":28,"updatedAt":28,"parent":11,"children":29,"_count":30},"career","Career & Growth","Professional development for developers","2026-07-24T06:02:45.486Z",[],{"posts":31},3,{"id":33,"name":34,"description":35,"parentId":11,"createdAt":36,"updatedAt":36,"parent":11,"children":37,"_count":38},"opinion","Opinion & Analysis","Industry analysis and technical opinions","2026-07-24T06:02:45.272Z",[],{"posts":39},6,{"id":41,"name":42,"description":43,"parentId":11,"createdAt":44,"updatedAt":44,"parent":11,"children":45,"_count":46},"tutorials","Tutorials","Step-by-step learning guides","2026-07-24T06:02:44.965Z",[],{"posts":47},21,{"id":49,"name":50,"description":51,"parentId":11,"createdAt":52,"updatedAt":52,"parent":11,"children":53,"_count":54},"software-engineering","Software Engineering","Best practices, patterns, and principles","2026-07-24T06:02:44.689Z",[],{"posts":55},24,{"id":57,"name":58,"description":59,"parentId":11,"createdAt":60,"updatedAt":60,"parent":11,"children":61,"_count":62},"devops-cloud","DevOps & Cloud","Infrastructure, deployment, and cloud computing","2026-07-24T06:02:44.453Z",[],{"posts":39},{"id":64,"name":65,"description":66,"parentId":11,"createdAt":67,"updatedAt":67,"parent":11,"children":68,"_count":69},"web-development","Web Development","Frontend and backend web development tutorials and guides","2026-07-24T06:02:44.169Z",[],{"posts":55},{"page":15,"limit":71,"total":71,"totalPages":15,"hasNext":72,"hasPrev":72},8,false,{"success":4,"data":74},{"id":75,"slug":76,"title":77,"excerpt":78,"content":79,"status":80,"visibility":81,"featuredImage":82,"canonicalUrl":11,"readingTime":83,"viewCount":84,"commentEnabled":4,"publishedAt":85,"scheduledAt":11,"createdAt":86,"updatedAt":87,"seoTitle":88,"seoDescription":78,"seoKeywords":11,"authorId":89,"author":90,"coAuthors":95,"tags":96,"categories":112,"comments":115,"_count":116,"relatedPosts":118},"50add5fc-4026-4267-b917-7f71ea9e35b0","complete-guide-vue3-composables","The Complete Guide to Vue 3 Composables","Understand the power of Vue 3's Composition API through practical, real-world composable patterns that you can use today.","## What Are Composables?\n\nComposables are functions that leverage Vue's Composition API to encapsulate and reuse stateful logic. Think of them as React hooks, but with Vue's reactivity system.\n\n## Your First Composable\n\n```typescript\n\u002F\u002F composables\u002FuseDarkMode.ts\nexport function useDarkMode() {\n  const isDark = ref(false)\n\n  function toggle() {\n    isDark.value = !isDark.value\n    document.documentElement.classList.toggle('dark', isDark.value)\n  }\n\n  onMounted(() => {\n    isDark.value = document.documentElement.classList.contains('dark')\n  })\n\n  return { isDark, toggle }\n}\n```\n\n## Real-World Pattern: Data Fetching\n\n```typescript\n\u002F\u002F composables\u002FuseApi.ts\nexport function useApi\u003CT>(url: MaybeRef\u003Cstring>) {\n  const data = ref\u003CT | null>(null)\n  const error = ref\u003CError | null>(null)\n  const loading = ref(false)\n\n  async function execute() {\n    loading.value = true\n    error.value = null\n    try {\n      const response = await fetch(unref(url))\n      if (!response.ok) throw new Error(response.statusText)\n      data.value = await response.json()\n    } catch (e) {\n      error.value = e as Error\n    } finally {\n      loading.value = false\n    }\n  }\n\n  \u002F\u002F Auto-fetch when URL changes\n  watch(() => unref(url), execute, { immediate: true })\n\n  return { data, error, loading, refresh: execute }\n}\n```\n\n::callout{icon=\"i-lucide-info\" color=\"info\"}\nThe key insight: composables encapsulate **reactive state + logic** together. They're not just utility functions — they maintain state across component lifecycles.\n::\n\n## Advanced: Shared State Composable\n\n```typescript\n\u002F\u002F composables\u002FuseAuth.ts\nconst currentUser = ref\u003CUser | null>(null)\nconst isAuthenticated = computed(() => !!currentUser.value)\n\nexport function useAuth() {\n  async function login(credentials: LoginInput) {\n    const user = await $fetch('\u002Fapi\u002Fauth\u002Flogin', {\n      method: 'POST',\n      body: credentials,\n    })\n    currentUser.value = user\n  }\n\n  function logout() {\n    currentUser.value = null\n    navigateTo('\u002Flogin')\n  }\n\n  return {\n    currentUser: readonly(currentUser),\n    isAuthenticated,\n    login,\n    logout,\n  }\n}\n```\n\n## Composable Best Practices\n\n1. **Prefix with `use`** — Convention makes composables instantly recognizable\n2. **Return refs, not raw values** — Maintains reactivity when destructured\n3. **Accept `MaybeRef` params** — Makes composables flexible\n4. **Clean up side effects** — Use `onUnmounted` for intervals, listeners, etc.\n5. **Keep them focused** — One composable, one responsibility\n\n## Testing Composables\n\n```typescript\nimport { describe, it, expect } from 'vitest'\nimport { useDarkMode } from '.\u002FuseDarkMode'\n\ndescribe('useDarkMode', () => {\n  it('toggles dark mode', () => {\n    const { isDark, toggle } = useDarkMode()\n    expect(isDark.value).toBe(false)\n    toggle()\n    expect(isDark.value).toBe(true)\n  })\n})\n```\n\nComposables are the backbone of modern Vue development. Master them, and you'll write cleaner, more testable, more reusable code.\n","published","public","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1517694712202-14dd9538aa97?w=1200&q=80",12,990,"2026-07-19T06:02:51.753Z","2026-07-24T06:02:51.757Z","2026-07-28T13:17:36.026Z","The Complete Guide to Vue 3 Composables | BitBlog","fddb5d93-7a2c-4d86-a06a-fa32e73a01c6",{"email":91,"bio":92,"id":89,"name":93,"avatarUrl":94},"mbeahessilfieprince@gmail.com","Fullstack Software Developer ","Mbeah Essilfie","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F93322394?v=4",[],[97,102,107],{"id":98,"name":99,"color":100,"description":101},"javascript","JavaScript","#f7df1e","The language of the web",{"id":103,"name":104,"color":105,"description":106},"typescript","TypeScript","#3178c6","Typed superset of JavaScript",{"id":108,"name":109,"color":110,"description":111},"vue","Vue","#4fc08d","The Progressive JavaScript Framework",[113,114],{"id":64,"name":65,"description":66},{"id":41,"name":42,"description":43},[],{"comments":117},0,[119,128,138],{"id":120,"slug":121,"title":122,"excerpt":123,"featuredImage":124,"viewCount":125,"readingTime":71,"publishedAt":126,"author":127},"2997028f-4d22-4eb7-9d86-894a54cb559a","building-type-safe-apis-nuxt3-prisma","Building Type-Safe APIs with Nuxt 3 and Prisma","Learn how to build end-to-end type-safe APIs using Nuxt 3 server routes and Prisma ORM for a seamless developer experience.","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1498050108023-c5249f4df085?w=1200&q=80",1922,"2026-07-23T06:02:46.910Z",{"id":89,"name":93,"avatarUrl":94},{"id":129,"slug":130,"title":131,"excerpt":132,"featuredImage":133,"viewCount":134,"readingTime":135,"publishedAt":136,"author":137},"da9e4553-8b0d-411e-b4ec-f9684017e163","mastering-tailwind-css-design-systems","Mastering Tailwind CSS: From Utility Classes to Design Systems","Go beyond basic utility classes and learn how to build cohesive, maintainable design systems with Tailwind CSS.","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1555066931-4365d14bab8c?w=1200&q=80",257,10,"2026-07-21T06:02:49.268Z",{"id":89,"name":93,"avatarUrl":94},{"id":139,"slug":140,"title":141,"excerpt":142,"featuredImage":143,"viewCount":144,"readingTime":145,"publishedAt":146,"author":147},"8570ed3d-b1cf-4288-8578-aed5593f8c58","docker-for-frontend-developers","Docker for Frontend Developers: A Practical Introduction","Stop saying 'it works on my machine.' Learn Docker fundamentals through the lens of frontend development workflows.","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1551288049-bebda4e38f71?w=1200&q=80",409,9,"2026-07-17T06:02:53.993Z",{"id":89,"name":93,"avatarUrl":94},{"data":149,"body":151,"toc":341},{"title":150,"description":150},"",{"type":152,"children":153},"root",[154,163,169,175,187,193,201,220,226,234,240,317,323,331,336],{"type":155,"tag":156,"props":157,"children":159},"element","h2",{"id":158},"what-are-composables",[160],{"type":161,"value":162},"text","What Are Composables?",{"type":155,"tag":164,"props":165,"children":166},"p",{},[167],{"type":161,"value":168},"Composables are functions that leverage Vue's Composition API to encapsulate and reuse stateful logic. Think of them as React hooks, but with Vue's reactivity system.",{"type":155,"tag":156,"props":170,"children":172},{"id":171},"your-first-composable",[173],{"type":161,"value":174},"Your First Composable",{"type":155,"tag":176,"props":177,"children":181},"pre",{"className":178,"code":179,"language":103,"meta":150,"style":180},"language-typescript","\u002F\u002F composables\u002FuseDarkMode.ts\nexport function useDarkMode() {\n  const isDark = ref(false)\n\n  function toggle() {\n    isDark.value = !isDark.value\n    document.documentElement.classList.toggle('dark', isDark.value)\n  }\n\n  onMounted(() => {\n    isDark.value = document.documentElement.classList.contains('dark')\n  })\n\n  return { isDark, toggle }\n}\n","undefined",[182],{"type":155,"tag":183,"props":184,"children":185},"code",{"__ignoreMap":150},[186],{"type":161,"value":179},{"type":155,"tag":156,"props":188,"children":190},{"id":189},"real-world-pattern-data-fetching",[191],{"type":161,"value":192},"Real-World Pattern: Data Fetching",{"type":155,"tag":176,"props":194,"children":196},{"className":178,"code":195,"language":103,"meta":150,"style":180},"\u002F\u002F composables\u002FuseApi.ts\nexport function useApi\u003CT>(url: MaybeRef\u003Cstring>) {\n  const data = ref\u003CT | null>(null)\n  const error = ref\u003CError | null>(null)\n  const loading = ref(false)\n\n  async function execute() {\n    loading.value = true\n    error.value = null\n    try {\n      const response = await fetch(unref(url))\n      if (!response.ok) throw new Error(response.statusText)\n      data.value = await response.json()\n    } catch (e) {\n      error.value = e as Error\n    } finally {\n      loading.value = false\n    }\n  }\n\n  \u002F\u002F Auto-fetch when URL changes\n  watch(() => unref(url), execute, { immediate: true })\n\n  return { data, error, loading, refresh: execute }\n}\n",[197],{"type":155,"tag":183,"props":198,"children":199},{"__ignoreMap":150},[200],{"type":161,"value":195},{"type":155,"tag":202,"props":203,"children":206},"callout",{"color":204,"icon":205},"info","i-lucide-info",[207],{"type":155,"tag":164,"props":208,"children":209},{},[210,212,218],{"type":161,"value":211},"The key insight: composables encapsulate ",{"type":155,"tag":213,"props":214,"children":215},"strong",{},[216],{"type":161,"value":217},"reactive state + logic",{"type":161,"value":219}," together. They're not just utility functions — they maintain state across component lifecycles.",{"type":155,"tag":156,"props":221,"children":223},{"id":222},"advanced-shared-state-composable",[224],{"type":161,"value":225},"Advanced: Shared State Composable",{"type":155,"tag":176,"props":227,"children":229},{"className":178,"code":228,"language":103,"meta":150,"style":180},"\u002F\u002F composables\u002FuseAuth.ts\nconst currentUser = ref\u003CUser | null>(null)\nconst isAuthenticated = computed(() => !!currentUser.value)\n\nexport function useAuth() {\n  async function login(credentials: LoginInput) {\n    const user = await $fetch('\u002Fapi\u002Fauth\u002Flogin', {\n      method: 'POST',\n      body: credentials,\n    })\n    currentUser.value = user\n  }\n\n  function logout() {\n    currentUser.value = null\n    navigateTo('\u002Flogin')\n  }\n\n  return {\n    currentUser: readonly(currentUser),\n    isAuthenticated,\n    login,\n    logout,\n  }\n}\n",[230],{"type":155,"tag":183,"props":231,"children":232},{"__ignoreMap":150},[233],{"type":161,"value":228},{"type":155,"tag":156,"props":235,"children":237},{"id":236},"composable-best-practices",[238],{"type":161,"value":239},"Composable Best Practices",{"type":155,"tag":241,"props":242,"children":243},"ol",{},[244,261,271,289,307],{"type":155,"tag":245,"props":246,"children":247},"li",{},[248,259],{"type":155,"tag":213,"props":249,"children":250},{},[251,253],{"type":161,"value":252},"Prefix with ",{"type":155,"tag":183,"props":254,"children":256},{"className":255},[],[257],{"type":161,"value":258},"use",{"type":161,"value":260}," — Convention makes composables instantly recognizable",{"type":155,"tag":245,"props":262,"children":263},{},[264,269],{"type":155,"tag":213,"props":265,"children":266},{},[267],{"type":161,"value":268},"Return refs, not raw values",{"type":161,"value":270}," — Maintains reactivity when destructured",{"type":155,"tag":245,"props":272,"children":273},{},[274,287],{"type":155,"tag":213,"props":275,"children":276},{},[277,279,285],{"type":161,"value":278},"Accept ",{"type":155,"tag":183,"props":280,"children":282},{"className":281},[],[283],{"type":161,"value":284},"MaybeRef",{"type":161,"value":286}," params",{"type":161,"value":288}," — Makes composables flexible",{"type":155,"tag":245,"props":290,"children":291},{},[292,297,299,305],{"type":155,"tag":213,"props":293,"children":294},{},[295],{"type":161,"value":296},"Clean up side effects",{"type":161,"value":298}," — Use ",{"type":155,"tag":183,"props":300,"children":302},{"className":301},[],[303],{"type":161,"value":304},"onUnmounted",{"type":161,"value":306}," for intervals, listeners, etc.",{"type":155,"tag":245,"props":308,"children":309},{},[310,315],{"type":155,"tag":213,"props":311,"children":312},{},[313],{"type":161,"value":314},"Keep them focused",{"type":161,"value":316}," — One composable, one responsibility",{"type":155,"tag":156,"props":318,"children":320},{"id":319},"testing-composables",[321],{"type":161,"value":322},"Testing Composables",{"type":155,"tag":176,"props":324,"children":326},{"className":178,"code":325,"language":103,"meta":150,"style":180},"import { describe, it, expect } from 'vitest'\nimport { useDarkMode } from '.\u002FuseDarkMode'\n\ndescribe('useDarkMode', () => {\n  it('toggles dark mode', () => {\n    const { isDark, toggle } = useDarkMode()\n    expect(isDark.value).toBe(false)\n    toggle()\n    expect(isDark.value).toBe(true)\n  })\n})\n",[327],{"type":155,"tag":183,"props":328,"children":329},{"__ignoreMap":150},[330],{"type":161,"value":325},{"type":155,"tag":164,"props":332,"children":333},{},[334],{"type":161,"value":335},"Composables are the backbone of modern Vue development. Master them, and you'll write cleaner, more testable, more reusable code.",{"type":155,"tag":337,"props":338,"children":339},"style",{},[340],{"type":161,"value":150},{"title":150,"searchDepth":342,"depth":342,"links":343},2,[344,345,346,347,348,349],{"id":158,"depth":342,"text":162},{"id":171,"depth":342,"text":174},{"id":189,"depth":342,"text":192},{"id":222,"depth":342,"text":225},{"id":236,"depth":342,"text":239},{"id":319,"depth":342,"text":322}]