Remix Vite vs Remix Classic - Making the Right Choice
A comprehensive comparison between Remix Vite and Remix Classic, exploring performance differences, developer experience, and migration considerations.
Remix Vite vs Remix Classic: Understanding the Differences
Remix has undergone a significant transformation with the introduction of its Vite-based build system. If you're trying to decide between Remix Vite and Remix Classic for your next project (or considering a migration), this comparison will help you make an informed decision.
Build System Architecture
Remix Classic
Remix Classic uses a custom build system built on esbuild:
// remix.config.js (Classic)
module.exports = {
appDirectory: 'app',
assetsBuildDirectory: 'public/build',
serverBuildPath: 'build/index.js',
publicPath: '/build/',
serverModuleFormat: 'cjs',
future: {
v2_errorBoundary: true,
v2_meta: true,
v2_normalizeFormMethod: true,
v2_routeConvention: true,
},
}This approach provided a stable foundation but lacked some modern tooling capabilities and had slower build times for larger projects.
Remix Vite
Remix Vite leverages Vite's powerful build system:
// vite.config.ts
import { vitePlugin as remix } from '@remix-run/dev'
import { defineConfig } from 'vite'
import tsconfigPaths from 'vite-tsconfig-paths'
export default defineConfig({
plugins: [remix(), tsconfigPaths()],
})The Vite integration brings numerous benefits including:
- Significantly faster builds and HMR
- Better ecosystem compatibility
- Access to Vite's extensive plugin ecosystem
Development Experience
Hot Module Replacement (HMR)
One of the most noticeable differences is in HMR performance:
Remix Classic:
- Page refreshes for most changes
- Slower feedback cycle
- State is lost between refreshes
Remix Vite:
- True HMR that preserves component state
- Near-instant updates
- Better developer flow with fewer interruptions
# Remix Vite HMR is approximately 5-10x faster
# Classic: ~800-1500ms refresh time
# Vite: ~100-200ms update timeDevelopment Server Startup
The development server startup time is dramatically different:
Remix Classic:
- Initial startup: 5-10 seconds (depending on project size)
- Subsequent startup: 3-5 seconds
Remix Vite:
- Initial startup: 2-3 seconds
- Subsequent startup: less than 1 second
Performance Optimizations
Code Splitting
Both versions support code splitting, but with different implementations:
Remix Classic:
// app/routes/my-route.jsx (Classic)
import { lazy } from 'react'
const LazyComponent = lazy(() => import('../components/heavy-component'))
export default function MyRoute() {
return (
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
)
}Remix Vite:
// app/routes/my-route.jsx (Vite)
// Automatic code splitting at the route level works out of the box
import HeavyComponent from '../components/heavy-component'
export default function MyRoute() {
return <HeavyComponent />
}Vite's handling of imports and code splitting is more efficient and requires less manual optimization.
Build Output
The production build output differs in structure and optimization level:
Remix Classic:
- Single server bundle
- Client bundles with manual code splitting
- Required more manual optimization
Remix Vite:
- Optimized server chunks
- Automatic client-side code splitting
- Better tree-shaking and dead code elimination
Feature Compatibility
Server Components
With both Remix versions moving toward React Server Components (RSC) support:
Remix Classic:
- Limited RSC support
- Requires more workarounds
Remix Vite:
- Better positioned for RSC integration
- More aligned with React's future direction
TypeScript Integration
TypeScript support has improved significantly with Vite:
Remix Classic:
// Classic often required manual type assertions
const data = useLoaderData() as { user: User }Remix Vite:
// Vite has better type inference
const data = useLoaderData<typeof loader>()Migration Considerations
If you're considering migrating from Classic to Vite, here are key points to consider:
Config Changes
You'll need to replace remix.config.js with vite.config.ts:
// vite.config.ts
import { vitePlugin as remix } from '@remix-run/dev'
import { defineConfig } from 'vite'
import tsconfigPaths from 'vite-tsconfig-paths'
export default defineConfig({
plugins: [remix()],
// Vite-specific options here
})Server Adapters
Server adapter handling is different in Vite:
// server.ts (Vite)
import { createRequestHandler } from '@remix-run/express'
import { installGlobals } from '@remix-run/node'
installGlobals()
const viteDevServer =
process.env.NODE_ENV === 'production'
? undefined
: await import('vite').then((vite) =>
vite.createServer({
server: { middlewareMode: true },
})
)
app.all(
'*',
createRequestHandler({
build: viteDevServer
? () => viteDevServer.ssrLoadModule('virtual:remix/server-build')
: await import('./build/server/index.js'),
})
)Common Migration Issues
Some common issues to watch for:
- Path Resolution: Differences in how Vite resolves imports
- Asset Handling: Changes in static asset imports
- Environment Variables: Need to prefix with
VITE_for client access
Performance Benchmarks
In our testing, Remix Vite showed significant performance improvements:
| Metric | Remix Classic | Remix Vite | Improvement |
|---|---|---|---|
| Dev Startup | 8.5s | 2.1s | 75% faster |
| HMR Speed | 1.2s | 0.15s | 87% faster |
| Build Time | 45s | 18s | 60% faster |
| Bundle Size | 320KB | 275KB | 14% smaller |
Conclusion
When to Choose Remix Classic:
- Established projects where migration costs outweigh benefits
- Projects with specific plugins only available for Classic
- Teams deeply familiar with the Classic workflow
When to Choose Remix Vite:
- New projects starting from scratch
- Performance-sensitive development environments
- Projects leveraging modern Vite plugins and ecosystem
Remix Vite represents the future direction of the framework, with significant performance advantages and better alignment with the React ecosystem. For most new projects, it's the recommended choice, while existing projects should weigh the migration benefits against the required effort.