Skip to main content
Yosavo Studio by Youssef Ibaichou , home
Expertise

Vue.js

Vue.js has been part of several of my production applications, particularly SaaS platforms and business systems built alongside Laravel. I use it for product interfaces where maintainability, component boundaries, API integration and long-term evolution matter.

Composition API · State ownership · Laravel + Vue · Accessibility

How I think about it

How I think about Vue

Vue makes it easy to start quickly, but maintainability still depends on how components, state, business logic and API interactions are structured as the product grows.

  • Components own one job; pages compose them.
  • Composables hold behaviour; components hold markup and wiring.
  • A store is for state that is genuinely shared — not the default home for data.
  • Business rules live on the server; the frontend reflects them.

Key experience

Where this comes from

  • Lead developer on a multi-tenant collaboration platform with a Vue.js frontend and a Laravel backend, including the frontend integration with its REST APIs.
  • Web applications and internal business systems built with Laravel and Vue.js across my full-stack and lead developer roles.

Scope

What I know

  • Component boundaries
  • Composition API & composables
  • Local vs shared vs store state
  • API integration
  • Pagination & filters
  • Complex forms
  • Laravel + Vue architecture
  • Routing & guards
  • Accessible SPAs
  • Lazy loading & code splitting
  • Maintainable structure

Deep dive

Vue.js in practice

On this page (10 topics)

Component architecture

Single-file components make it pleasantly easy to keep markup, behaviour and styles together — which is also how 800-line components happen. I split by responsibility: page components that load data and compose a screen, domain components that understand one piece of the product (an invoice table, a member list), and reusable UI components that know nothing about the business.

Abstraction comes after real repetition. A generic <DataTable> with thirty props is harder to change than three focused tables sharing a few small building blocks.

Composition API

Composables are where the Composition API pays off: behaviour — loading members, managing filter state, tracking an upload — moves out of components into functions that can be reused and tested on their own. Components become mostly markup and wiring.

Lifecycle concerns travel with the composable. If it starts a timer, subscribes to an event or opens a connection, it also cleans up in onUnmounted, so the component using it can't forget.

A composable owning loading, error and data state
// composables/useMembers.ts — behaviour, no markup
export function useMembers(organizationId: Ref<number>) {
  const members = ref<Member[]>([]);
  const loading = ref(false);
  const error = ref<string | null>(null);

  async function load() {
    loading.value = true;
    error.value = null;
    try {
      members.value = await api.get(`/organizations/${organizationId.value}/members`);
    } catch {
      error.value = "Members could not be loaded.";
    } finally {
      loading.value = false;
    }
  }

  watch(organizationId, load, { immediate: true });
  return { members, loading, error, reload: load };
}

State management

Local state stays in the component. State shared by a parent and its children travels through props and events. A store is for state that many unrelated parts of the app read — the authenticated user, the active organization, permissions, feature flags.

Putting everything in a global store feels organised at first and becomes a coupling problem later: any component can change anything, and nobody knows what depends on what. Server data is better handled as cached request state close to where it's used than copied into a store by hand.

API integration

Every screen that loads data needs loading, empty, error and success states. List screens add pagination and filters that should survive a refresh — so filters belong in the URL query, not only in component state.

Authentication, error parsing and retries for idempotent requests live in one API client. Stale responses are a classic SPA bug: when filters change quickly, an older request can resolve last and overwrite newer results, so superseded requests are cancelled or ignored.

Forms

Complex forms — dynamic rows, conditional sections, multi-step flows — need one explicit form model rather than many unrelated refs. Client-side validation gives quick feedback; the server's validation response is still the source of truth, and its field errors map back onto the same inputs.

Errors are shown next to the field and linked with aria-describedby, and focus moves to the first invalid field on submit.

Laravel + Vue

Laravel and Vue work well together when the boundary is explicit. Laravel owns business rules, authorization and validation; Vue owns interaction and presentation and consumes a documented API. Duplicating business rules in the frontend — price calculations, permission logic — guarantees they will drift apart.

For a first-party SPA on the same domain, session-based authentication with CSRF protection is usually simpler and safer than storing tokens in the browser. Laravel's validation errors come back in one consistent shape the form layer understands. Deployment needs care too: frontend assets are versioned with the backend release, so an old cached bundle never talks to a changed API.

  1. Vue

    interaction, presentation

  2. API

    validated, authorized

  3. Laravel domain

    business rules

  4. Database

    tenant-scoped data

Laravel + Vue boundary: Vue handles interaction and presentation, the API validates and authorizes every request, Laravel owns business rules, and the database is only reached through the backend.

Related: Laravel expertise

Routing

Routes mirror product areas, with nested routes for sections that share a layout — an organization's settings, a project and its tabs. Route-level data loading gives each page clear loading and error states. Navigation guards hide areas a user can't access, but they're a convenience: the API still enforces every permission.

Navigation state worth keeping — the active tab, filters, the current page — belongs in the URL, so it survives a refresh and can be shared.

Accessibility

Vue renders whatever markup you give it, so accessibility comes down to the components: native elements first, dialogs that move and return focus, menus and custom selects that follow established keyboard patterns, and live regions for asynchronous updates. In a single-page app, route changes should update the document title and move focus, or screen-reader users won't know the page changed.

Related: Web Accessibility expertise

Performance

Route-level lazy loading keeps the initial bundle small. Long lists need stable keys and, beyond a few hundred rows, pagination or virtualization rather than rendering everything. Making large data structures deeply reactive is a hidden cost — data that's only displayed doesn't need fine-grained reactivity. Beyond that, I optimise what the profiler shows, not what looks slow in code review.

Maintainability

Consistency matters more than any single pattern: one way to call the API, one way to handle errors, one place for form logic, one folder structure organised by feature. Composables and shared components remove duplication only when they're genuinely shared; otherwise they add indirection.

In practice

Common problems I work with

  • Giant components mixing data fetching, business rules and markup
  • A global store every component reads and writes
  • Validation and business rules duplicated between Laravel and Vue
  • Race conditions when filters change faster than requests return
  • Dialogs and custom selects that don't work with a keyboard
  • Slow first loads from one large bundle

Real products

How it connects to real products

Vue.js has been the frontend for Laravel-based platforms and business systems I've built and led.

  • Enterprise SaaS

    Multi-Tenant Collaboration Platform

    Lead developer on a Laravel and Vue.js platform — the Vue frontend integrated with the platform's REST APIs in a multi-tenant, multi-user context.

    Private client project
  • Full-stack and lead developer roles

    Business applications & internal systems

    Web applications and internal business systems built with Laravel and Vue.js.

See all selected work

Related expertise

Laravel

Maintainable backends: structure, Eloquent, queues, authorization and SaaS patterns.

  • Application structure
  • Eloquent
  • Queues
  • Testing

SaaS Development

Tenancy, permissions, plans, billing and the realities of running SaaS in production.

  • Multi-tenancy
  • Permissions
  • Billing
  • Production delivery

Web Accessibility

Accessible engineering: semantics, keyboard and focus, forms, testing and WCAG in practice.

  • Semantic HTML
  • Keyboard & focus
  • Manual testing
  • WCAG

AWS

Workload-first cloud architecture: serverless, storage, data, security and cost.

  • Serverless
  • S3 & DynamoDB
  • IAM
  • Cost awareness

Related services

  • Custom Software Development

    Build reliable web platforms, SaaS products, dashboards, APIs, and internal tools with modern full-stack technologies.

    Learn more
  • SaaS Product Development

    Design and build SaaS products from MVP to production, including architecture, user management, subscriptions, integrations and cloud infrastructure.

    Learn more
Let's talk

Have a technical challenge?

Whether it's an architecture decision, a delivery problem or something specific to Vue.js, I can help you find a practical path forward.

What happens next

  1. Share your goal

    A few lines about the product, problem, or audit need.

  2. Get a quick read

    A practical reply on the best technical path forward.

  3. Move forward

    A clear scope, estimate, or concrete next step.