0
0
Vueframework~15 mins

Sharing composables across components in Vue - Deep Dive

Choose your learning style9 modes available
Overview - Sharing composables across components
What is it?
Sharing composables across components means creating reusable pieces of logic in Vue that multiple components can use. Composables are functions that use Vue's Composition API to encapsulate state and behavior. Instead of repeating code in each component, you write a composable once and import it wherever needed. This helps keep your code clean and organized.
Why it matters
Without sharing composables, developers would copy and paste the same logic in many components, making the app harder to maintain and update. Sharing composables saves time, reduces bugs, and makes your app easier to grow. It also encourages thinking about logic as reusable building blocks, which is a powerful way to write software.
Where it fits
Before learning this, you should understand Vue's Composition API basics, like reactive state and lifecycle hooks. After mastering sharing composables, you can explore advanced patterns like composable libraries, testing composables, and state management with composables.
Mental Model
Core Idea
A composable is a reusable function that packages reactive logic, and sharing it means multiple components can use the same logic without duplication.
Think of it like...
Sharing composables is like having a recipe card for a favorite dish that you can give to friends so they can cook the same meal without guessing the steps.
┌───────────────┐      ┌───────────────┐      ┌───────────────┐
│ Composable A  │─────▶│ Component 1   │      │ Component 2   │
│ (shared code) │      │ (uses logic)  │      │ (uses logic)  │
└───────────────┘      └───────────────┘      └───────────────┘
Build-Up - 7 Steps
1
FoundationUnderstanding Vue Composables Basics
🤔
Concept: Learn what a composable is and how it encapsulates reactive state and logic.
A composable is a plain JavaScript function that uses Vue's reactive APIs like ref() or reactive() to create state and behavior. For example, a composable can manage a counter with increment and reset functions. You call this function inside a component's setup() to get the reactive data and methods.
Result
You can create reactive state and functions inside a composable and use them inside a component.
Understanding that composables are just functions that return reactive data helps you see how easy it is to reuse logic.
2
FoundationImporting and Using Composables in Components
🤔
Concept: Learn how to import a composable function and use its returned state and methods inside a Vue component.
After creating a composable, you export it from a file. In your component, you import the composable and call it inside the setup() function. The returned reactive state and methods become part of the component's logic and template.
Result
Multiple components can use the same composable by importing and calling it.
Knowing that composables are imported like normal functions makes sharing logic straightforward and natural.
3
IntermediateSharing State vs Independent Instances
🤔Before reading on: do you think importing a composable shares the same state instance across components or creates separate ones? Commit to your answer.
Concept: Understand that each time you call a composable, it creates a new independent state instance unless you explicitly share it.
When you import and call a composable in different components, each call runs the function anew, creating separate reactive states. This means components do not share state by default. To share state, you must create a shared store or use a singleton pattern inside the composable.
Result
By default, composables provide isolated state per component, preventing unintended shared mutations.
Knowing that composables create fresh state each call prevents bugs caused by unexpected shared data.
4
IntermediateCreating Shared State with Singletons
🤔Before reading on: can you guess how to make a composable share state across components? Commit to your answer.
Concept: Learn how to create a shared reactive state inside a composable using a singleton pattern.
Inside the composable file, define a reactive state variable outside the composable function. The composable function returns this shared state and methods. Because the state is outside the function, all components importing and calling the composable get the same reactive state instance.
Result
Multiple components share and react to the same state, enabling synchronized behavior.
Understanding how JavaScript module scope works with Vue reactivity enables you to share state safely and intentionally.
5
IntermediatePassing Parameters to Customize Composables
🤔
Concept: Learn how to make composables flexible by accepting parameters to customize their behavior per component.
Composables can accept arguments like initial values or options. This lets each component customize the composable's behavior while still reusing the core logic. For example, a useCounter composable can accept a starting number so different components start counting from different values.
Result
You can reuse the same composable logic with different settings in multiple components.
Knowing how to parameterize composables makes them more versatile and reduces the need for many similar composables.
6
AdvancedHandling Side Effects and Cleanup in Composables
🤔Before reading on: do you think composables automatically clean up side effects like event listeners? Commit to your answer.
Concept: Learn how to manage side effects and cleanup inside composables using Vue lifecycle hooks.
Composables can use onMounted, onUnmounted, or watch to handle side effects like event listeners or timers. Cleanup code should run in onUnmounted to avoid memory leaks. This ensures composables behave well when components mount and unmount.
Result
Composables manage their own lifecycle, preventing bugs and resource leaks.
Understanding lifecycle management inside composables is key to building robust reusable logic.
7
ExpertOptimizing Composables for Performance and Reusability
🤔Before reading on: do you think all composables should always create new reactive state? Commit to your answer.
Concept: Explore advanced patterns like memoization, lazy initialization, and composable factories to optimize performance and reuse.
Sometimes you want composables to share expensive computations or avoid unnecessary reactive state creation. You can memoize results or create composable factories that return composables with preset parameters. Also, be mindful of reactive dependencies to avoid redundant updates.
Result
Your composables become efficient and scalable in large applications.
Knowing advanced optimization techniques helps you write composables that perform well and scale gracefully.
Under the Hood
Vue composables are plain JavaScript functions that use Vue's reactive APIs like ref() and reactive() to create reactive state. When a composable is called inside a component's setup(), Vue tracks the reactive dependencies and updates the component when state changes. The module system caches imported files, so variables declared outside the composable function act as singletons shared across imports. Lifecycle hooks inside composables connect to the component's lifecycle, allowing setup and cleanup of side effects.
Why designed this way?
Vue's Composition API was designed to solve the problem of code reuse and organization in complex components. By using plain functions with reactive state, Vue allows flexible logic sharing without forcing inheritance or mixins, which were harder to manage. The module scope singleton pattern leverages JavaScript's module caching to enable shared state when needed, giving developers control over isolation vs sharing.
┌─────────────────────────────┐
│ Vue Component Setup()       │
│ ┌─────────────────────────┐ │
│ │ Calls Composable Func   │ │
│ │ ┌─────────────────────┐ │ │
│ │ │ Uses ref(), reactive │ │ │
│ │ │ Returns reactive obj │ │ │
│ │ └─────────────────────┘ │ │
│ └─────────────────────────┘ │
│                             │
│ Vue Reactivity System tracks │
│ dependencies and updates UI │
└─────────────────────────────┘

Module Scope Variables (outside composable func) act as shared singletons across imports.
Myth Busters - 4 Common Misconceptions
Quick: Does importing a composable automatically share its state across all components? Commit to yes or no.
Common Belief:Importing a composable shares the same reactive state instance across all components using it.
Tap to reveal reality
Reality:Each call to a composable function creates a new reactive state instance unless the state is defined outside the function to be shared.
Why it matters:Assuming shared state by default can cause unexpected bugs where components do not see each other's changes or accidentally share state when not intended.
Quick: Can composables only be used inside setup()? Commit to yes or no.
Common Belief:Composables must be called only inside the setup() function of components.
Tap to reveal reality
Reality:While composables are designed for setup(), they can also be used in other composables or even outside components for logic reuse, as long as Vue's reactivity context is respected.
Why it matters:Believing composables are limited to setup() restricts their reuse and composability in larger logic structures.
Quick: Do composables automatically clean up side effects like event listeners? Commit to yes or no.
Common Belief:Composables automatically handle cleanup of side effects when components unmount.
Tap to reveal reality
Reality:Composables must explicitly use lifecycle hooks like onUnmounted to clean up side effects; otherwise, memory leaks can occur.
Why it matters:Ignoring cleanup leads to bugs and performance issues in long-running applications.
Quick: Is it always better to share state in composables? Commit to yes or no.
Common Belief:Sharing state in composables is always the best practice for code reuse.
Tap to reveal reality
Reality:Sharing state is useful only when components need synchronized data; otherwise, isolated state per component is safer and avoids unintended side effects.
Why it matters:Misusing shared state can cause hard-to-debug bugs due to unexpected data changes across components.
Expert Zone
1
Composables can be designed as factories that return new composables with preset parameters, enabling flexible logic composition.
2
Reactive dependencies inside composables should be carefully managed to avoid unnecessary recomputations or infinite update loops.
3
Using module scope variables for shared state requires careful consideration of app lifecycle and potential memory leaks.
When NOT to use
Avoid sharing state in composables when components require independent state or when global state management solutions like Pinia or Vuex are more appropriate. For very large apps, dedicated state stores provide better tooling and debugging.
Production Patterns
In production, composables are often organized into libraries for authentication, API calls, or UI logic. Shared state composables act like lightweight stores for small shared data. Developers combine composables with Pinia for complex state and use testing frameworks to verify composable behavior.
Connections
React Hooks
Similar pattern of reusable logic encapsulated in functions that manage state and side effects.
Understanding composables helps grasp React Hooks since both promote logic reuse via functions with reactive state.
Modular Programming
Composables are a form of modular code that encapsulates functionality for reuse and separation of concerns.
Knowing modular programming principles clarifies why composables improve maintainability and scalability.
Shared Resources in Operating Systems
Sharing state in composables is like managing shared resources in OS where access must be controlled to avoid conflicts.
Recognizing parallels with OS resource sharing highlights the importance of intentional shared state design to prevent bugs.
Common Pitfalls
#1Assuming composables share state by default causing unexpected data sharing.
Wrong approach:export function useCounter() { const count = ref(0); function increment() { count.value++ } return { count, increment }; } // Used in multiple components expecting shared count
Correct approach:const count = ref(0); export function useCounter() { function increment() { count.value++ } return { count, increment }; } // Shared count across components
Root cause:Defining reactive state inside the composable function creates new instances per call instead of sharing.
#2Not cleaning up side effects in composables leading to memory leaks.
Wrong approach:export function useWindowResize() { window.addEventListener('resize', onResize); function onResize() { /* update state */ } } // No cleanup on component unmount
Correct approach:import { onUnmounted } from 'vue'; export function useWindowResize() { window.addEventListener('resize', onResize); function onResize() { /* update state */ } onUnmounted(() => window.removeEventListener('resize', onResize)); } // Proper cleanup
Root cause:Ignoring Vue lifecycle hooks inside composables causes side effects to persist after component removal.
#3Passing reactive objects directly as parameters causing unexpected reactivity.
Wrong approach:export function useLogger(state) { watch(state, () => console.log('changed')); } // Passing reactive state from component directly
Correct approach:export function useLogger(state) { watch(() => state.value, () => console.log('changed')); } // Using getter function to avoid deep reactivity issues
Root cause:Passing reactive objects without care can cause deep watchers or infinite loops.
Key Takeaways
Composables are reusable functions that encapsulate reactive logic for Vue components.
By default, each call to a composable creates independent state; sharing state requires explicit design.
Lifecycle hooks inside composables manage side effects and cleanup to keep apps performant and bug-free.
Parameterizing composables makes them flexible and adaptable to different component needs.
Advanced patterns like singleton state and memoization optimize composables for real-world large apps.