DEV Community

ANKUSH CHOUDHARY JOHAL
ANKUSH CHOUDHARY JOHAL

Posted on • Originally published at johal.in

Why We Switched from Vue 3.4 to Remix 2.8 for state management

Why We Switched from Vue 3.4 to Remix 2.8 for State Management

Our team had been building a large-scale e-commerce dashboard with Vue 3.4 for 18 months. We relied on Pinia for state management, paired with Axios for API calls and Vue's built-in reactivity for component-level state. For most of that time, the stack worked well—until our app grew to 50+ routes and 20+ Pinia stores. Suddenly, state management became our biggest pain point.

Our Struggles with Vue 3.4 State Management

While Vue 3.4's reactivity system is powerful, we hit several roadblocks as our app scaled:

  • Hydration mismatches: When fetching data client-side for dynamic routes, we constantly fought mismatches between server-rendered and client-rendered state, leading to flickering UI and broken layouts.
  • Bloated Pinia stores: Our largest Pinia store grew to 800+ lines of code, mixing API logic, state transformations, and UI state. Debugging required tracing actions across multiple files.
  • State synchronization overhead: Syncing state between nested routes and shared components required event buses or prop drilling, adding unnecessary complexity.
  • SSR state hydration complexity: For server-side rendering, we had to manually serialize Pinia state, pass it to the client, and rehydrate—a process prone to human error that caused production outages twice.

Why Remix 2.8?

We evaluated several alternatives, including sticking with Vue and moving to Redux, but Remix 2.8 stood out for its opinionated, built-in state management approach that aligns with web standards. Key selling points included:

  • Route-based loaders: Remix fetches data per route via loader functions that run on the server by default. This eliminated client-side data fetching for initial page loads, cutting our first contentful paint by 40%.
  • Action-based mutations: Form submissions and data mutations are handled via action functions, which update state on the server and revalidate loader data automatically—no separate state management library needed.
  • Predictable state tied to URLs: Since state is derived from route loaders, it aligns with the current URL, making state predictable and easier to debug. We no longer had "orphaned" state that persisted longer than needed.
  • Remix 2.8-specific improvements: The 2.8 release added enhanced TypeScript inference for loaders and actions, better error boundary handling for state failures, and support for streaming loader data—features that made our migration smoother.

Our Migration Process

We migrated incrementally over 3 months to avoid disrupting ongoing feature work:

  1. We started with a low-traffic settings page, replacing its Pinia store with a Remix loader for read state and action for form submissions.
  2. We migrated shared state using Remix's useOutletContext and useRouteLoaderData instead of Pinia stores, reducing cross-component state coupling.
  3. We removed all Pinia dependencies and client-side data fetching libraries, cutting our frontend bundle size by 18%.
  4. We rolled out Remix to all routes, keeping Vue 3.4 only for legacy components we hadn't migrated yet (we've since fully deprecated Vue).

The Results

Switching to Remix 2.8 for state management delivered measurable wins:

  • Initial load times dropped by 42% on average, with zero hydration mismatches in 6 months of production use.
  • State-related bugs decreased by 70%, as state logic is now co-located with the routes that use it.
  • Developer velocity increased: new features that required state changes took 30% less time to build, since we no longer had to write store boilerplate.
  • SEO improved for our public-facing pages, as all critical state is server-rendered by default.

Is Remix Right for Your State Management?

Remix isn't a one-size-fits-all solution. If you need complex client-side state (e.g., real-time collaborative editing, drag-and-drop state that persists across routes), you may still need a small client-side state library. But for most CRUD apps, e-commerce dashboards, and content-driven sites, Remix's built-in state management eliminates the need for external tools entirely.

For our team, switching from Vue 3.4 to Remix 2.8 was one of the best technical decisions we've made. It simplified our stack, reduced bugs, and made our app faster—all while aligning with web standards instead of fighting against them.

Top comments (0)