DEV Community

Cover image for Writing Clean, Reusable Components in Vue 3 (Composition API): Best Practices
Muhammadamin
Muhammadamin

Posted on

Writing Clean, Reusable Components in Vue 3 (Composition API): Best Practices

Vue 3's Composition API offers a new way to organize and reuse logic in your components. By following some best practices, you can write clean, maintainable code that's easier to understand and extend. Let's dive in!


Image description


Use Composition Functions

Instead of scattering logic throughout your component, extract it into reusable functions using the Composition API. This keeps your component's structure clean and makes it easier to test.

<template>
  <div>
    <button @click="increment">Increment</button>
    <p>Count: {{ count }}</p>
  </div>
</template>

<script>
import { ref } from 'vue';

export default {
  setup() {
    const count = ref(0);

    const increment = () => {
      count.value++;
    };

    return { count, increment };
  },
};
</script>
Enter fullscreen mode Exit fullscreen mode

Use Reactive Variables

Use ref for simple reactive values and reactive for reactive objects. This ensures that changes to these values trigger reactivity in your component.

<template>
  <div>
    <p>Name: {{ user.name }}</p>
    <button @click="updateName">Update Name</button>
  </div>
</template>

<script>
import { reactive } from 'vue';

export default {
  setup() {
    const user = reactive({
      name: 'John Doe',
    });

    const updateName = () => {
      user.name = 'Jane Doe';
    };

    return { user, updateName };
  },
};
</script>
Enter fullscreen mode Exit fullscreen mode

Keep Components Small and Single-Purpose

Divide your components into smaller, single-purpose components. This makes your code easier to understand, test, and maintain.

<template>
  <div>
    <ProfileHeader :user="user" />
    <ProfilePosts :posts="posts" />
  </div>
</template>

<script>
import { ref } from 'vue';
import ProfileHeader from './ProfileHeader.vue';
import ProfilePosts from './ProfilePosts.vue';

export default {
  components: { ProfileHeader, ProfilePosts },
  setup() {
    const user = ref({ name: 'John Doe' });
    const posts = ref([]);

    // Fetch user data and posts

    return { user, posts };
  },
};
</script>
Enter fullscreen mode Exit fullscreen mode

Use Slots for Flexibility

Slots allow you to create components that are more flexible and can be customized by the parent component.

<template>
  <div>
    <slot name="header"></slot>
    <slot></slot>
  </div>
</template>

<script>
export default {
  name: 'Card',
};
</script>
Enter fullscreen mode Exit fullscreen mode

Use Composition Functions for Business Logic

Separate your business logic from your UI logic by using composition functions. This makes your code more modular and easier to test.

<script>
import { ref, computed } from 'vue';

export default {
  setup() {
    const count = ref(0);

    const increment = () => {
      count.value++;
    };

    const doubled = computed(() => count.value * 2);

    return { count, increment, doubled };
  },
};
</script>
Enter fullscreen mode Exit fullscreen mode

Use Custom Hooks for Shared Logic

Create custom hooks to encapsulate shared logic that can be reused across multiple components.

<script>
import { ref } from 'vue';

const useCounter = () => {
  const count = ref(0);

  const increment = () => {
    count.value++;
  };

  return { count, increment };
};

export default {
  setup() {
    const { count, increment } = useCounter();

    return { count, increment };
  },
};
</script>
Enter fullscreen mode Exit fullscreen mode

Use Teleport for Modal and Tooltip Components

Vue 3's Teleport feature allows you to render a component's children in a different part of the DOM, which is useful for creating modal and tooltip components.

<template>
  <teleport to="body">
    <div v-show="isOpen" class="modal">
      <slot></slot>
    </div>
  </teleport>
</template>

<script>
import { ref } from 'vue';

export default {
  setup() {
    const isOpen = ref(false);

    const openModal = () => {
      isOpen.value = true;
    };

    const closeModal = () => {
      isOpen.value = false;
    };

    return { isOpen, openModal, closeModal };
  },
};
</script>
Enter fullscreen mode Exit fullscreen mode

Use Provide/Inject for Dependency Injection

Use provide and inject to pass data and methods down through the component tree without having to manually pass props.

<script>
import { provide, inject } from 'vue';

const key = Symbol();

export const useStore = () => {
  const store = reactive({
    count: 0,
    increment() {
      store.count++;
    },
  });

  return store;
};

export const provideStore = () => {
  provide(key, useStore());
};

export const useInjectStore = () => {
  return inject(key);
};
</script>
Enter fullscreen mode Exit fullscreen mode

Use Composition API with Pinia for State Management

Pinia is a modern and elegant alternative to Vuex for managing state in Vue 3 applications. It integrates seamlessly with the Composition API, making it easy to create reactive stores and access them in your components.

<template>
  <div>
    <p>Count: {{ count }}</p>
    <button @click="increment">Increment</button>
    <button @click="decrement">Decrement</button>
  </div>
</template>

<script>
import { useCounterStore } from './store';

export default {
  setup() {
    const store = useCounterStore();

    return {
      count: store.count,
      increment: store.increment,
      decrement: store.decrement,
    };
  },
};
</script>
Enter fullscreen mode Exit fullscreen mode

👏Conclusion

By following these best practices, you can write cleaner, more reusable components in Vue 3 using the Composition API. This leads to more maintainable code and a better developer experience. 🚀

What are your thoughts on these practices? Do you have any other tips for writing clean, reusable components in Vue 3? Share them in the comments below!


Support the author ☕

I would be happy if u send me a coffee ☕

Top comments (0)