Vue.js Frontend Development with Nuxt 3, Pinia, and Composition API
Imagine you're building an SPA with dozens of screens on Vue 2 and Vuex. Mixins become unmanageable, performance drops, and type safety is lacking. Vue 3 with Composition API — not just a new version but a paradigm shift. We've migrated dozens of projects from Vue 2 to Vue 3 and know how to avoid common pitfalls. For example, on a project with 50+ components, we cut load time by 40% and code volume by 30% — all thanks to refactoring into composables and optimizing reactive dependencies.
Request a consultation to discuss migrating your project.
Problems We Solve
- Logic reuse: instead of mixins, we use composables — functions that can be combined like React hooks. Code becomes predictable and testable.
-
Type safety: TypeScript in Vue 3 is a first-class citizen.
defineComponentand<script setup lang="ts">provide autocompletion and type checking at every stage. - Performance: Vue 3 is up to 2x faster than Vue 2 thanks to Proxy-based reactivity and the v3 virtual DOM. We optimize Core Web Vitals: LCP, CLS, INP. This directly impacts search engine rankings.
- SSR/SEO: with Nuxt 3 you get SSR, SSG, ISR — pages are indexed, and LCP drops by 40–60%. Additionally, we use Suspense for loading state management.
Why Vue 3 Composition API is Better than Options API
Options API (data, methods, computed) forces splitting component logic across different options. As the component grows, this complicates maintenance. Composition API groups code by function:
<script setup lang="ts"> import { ref, computed, onMounted } from 'vue'; const count = ref(0); const doubled = computed(() => count.value * 2); async function fetchData() { // ... } onMounted(() => fetchData()); </script> <template> <div> <p>{{ count }} × 2 = {{ doubled }}</p> <button @click="count++">Increment</button> </div> </template> <script setup> is syntactic sugar — no need for return. Logic is reused through composables.
How Reactivity Works in Vue 3
Vue 3 uses Proxy for reactivity. Key primitives:
-
ref<T>()— for primitives and objects (accessed via.value) -
reactive({})— for objects (no.value) -
computed(() => ...)— computed value -
watch()/watchEffect()— side effects
More about choosing ref vs reactive
ref is simpler for single values and composability; reactive is convenient for objects with many fields. However, reactive cannot be replaced entirely, while ref can. In practice, composables return ref, and complex state is stored in Pinia.
const user = reactive({ name: '', email: '' }); // Vs const name = ref(''); // user.name vs name.value Composables and Logic Reuse
A composable is a function that uses the Composition API. Its name starts with use:
// composables/useAuth.ts import { ref } from 'vue'; export function useAuth() { const user = ref(null); const isLoggedIn = computed(() => !!user.value); async function login(credentials) { user.value = await api.login(credentials); } return { user, isLoggedIn, login }; } // In a component const { user, isLoggedIn, login } = useAuth(); Composables allow extracting authentication, API integration, geolocation, and more. The VueUse library contains 200+ ready composables: useStorage, useIntersectionObserver, useClipboard. Time savings on typical tasks — from 30%.
Pinia — State Management
Pinia is the official replacement for Vuex. Comparison:
| Criterion | Pinia | Vuex 4 |
|---|---|---|
| Type safety | TypeScript out of the box | Requires decorators |
| Composition support | Yes | Via useStore |
| Devtools | Official (Vue Devtools 6) | Vuex Devtools |
| Bundle size | ~1 KB | ~5 KB |
// stores/cart.ts import { defineStore } from 'pinia'; export const useCartStore = defineStore('cart', { state: () => ({ items: [] as CartItem[] }), getters: { total: (state) => state.items.reduce((sum, item) => sum + item.price, 0), }, actions: { addItem(item: CartItem) { this.items.push(item); }, }, }); // In a component const cart = useCartStore(); cart.addItem({ id: 1, price: 100 }); Vue Router 4
const routes = [ { path: '/', component: HomePage }, { path: '/blog/:slug', component: BlogPost, props: true }, { path: '/admin', component: AdminLayout, meta: { requiresAuth: true }, children: [ { path: 'users', component: UserList }, ] }, ]; // Navigation guard router.beforeEach((to) => { if (to.meta.requiresAuth && !auth.isLoggedIn) return '/login'; }); Comparison: Vue 2 Options API vs Vue 3 Composition API
| Aspect | Options API (Vue 2) | Composition API (Vue 3) |
|---|---|---|
| Logic organization | Split across options | Grouping by function |
| Type safety | Hard with TypeScript | First-class citizen |
| Reusability | Mixins (name conflicts) | Composables (no conflicts) |
| Performance | defineProperty (slower) | Proxy (up to 2x faster) |
Ecosystem
- VueUse — 200+ composables (useStorage, useIntersectionObserver)
- Quasar Framework — UI components + PWA/Electron
- PrimeVue — enterprise UI
- Vuelidate / VeeValidate — form validation
- Nuxt 3 — SSR/SSG/ISR framework, similar to Next.js
Our Process: How We Work and Timelines
- Analysis: review requirements, agree on architecture (SPA / SSR / SSG).
- Design: create component model, route schemas, prototypes.
- Development: write code in Vue 3 + TypeScript, use Pinia, cover with tests (Vitest).
- Testing: unit tests, e2e (Cypress), Core Web Vitals check.
- Deployment: set up CI/CD, publish to your hosting or Vercel.
Timelines: from 2 weeks for an SPA to 3 months for a complex application with real-time and analytics. Cost is assessed individually; typical starting price is $5,000 for a basic SPA.
What's Included
- Source code in Git (private repository)
- Component documentation (Storybook)
- Deployment and configuration instructions
- Demo session and team training
- Free support for the first month
Contact us to discuss your project. Get a consultation and estimate within one day. We guarantee code quality and deadline adherence. Our experience: 5+ years in Vue.js development, 50+ successful projects completed.







