Skip to content

Pareto 3.0

By childrentime · March 26, 2026

Pareto 3.0 is a ground-up rebuild of the framework. The bundler, the runtime, the state management, and the CLI have all been rewritten. This is the release we’ve been working toward: a lightweight React SSR framework that feels fast to use and fast to ship.

The biggest change in 3.0 is the build system. Pareto 2.x used Rspack (a Webpack-compatible bundler) with separate client and server configurations, Babel transforms, and a complex lazy compiler. All of that is gone.

Pareto 3.0 uses Vite 7 as its build engine. This means:

  • Instant dev server startup — Vite’s on-demand module transform means the dev server is ready in milliseconds, not seconds.
  • Native ESM in development — No bundling during dev. Modules are served directly to the browser.
  • React Fast Refresh — HMR that preserves component state, powered by @vitejs/plugin-react.
  • Your Vite plugins work — Any Vite plugin you already use (PostCSS, Tailwind, MDX, etc.) works out of the box. No framework-specific wrappers needed.
  • Single config surface — Customize the build by dropping a standard vite.config.ts in your project root. Pareto loads and merges it automatically. No more juggling separate Rspack configs for client and server.
vite.config.ts
import { defineConfig } from 'vite'
import myVitePlugin from 'my-vite-plugin'
export default defineConfig({
plugins: [myVitePlugin()],
})

Pareto 3.0 requires React 19. This gives you access to the latest React features:

  • use() hook — Read promises and context directly in render.
  • Actions — Async functions that integrate with transitions.
  • useOptimistic() — Optimistic UI updates built into React.
  • Improved Suspense — Better streaming and hydration behavior.

No server components — Pareto continues to use the loader pattern for server-side data fetching. Your components are standard React components that work on both server and client.

The file-based routing system has been refined. Convention files in 3.0:

FilePurpose
page.tsxRoute component
layout.tsxWrapping layout (nests from root to page)
loader.tsSeparate loader file for server-side data
head.tsxPer-route <title> and meta tags
not-found.tsx404 page (root level only)
error.tsxError page — catches loader and render errors
document.tsxDocument customizationgetDocumentProps() for <html> attributes
route.tsResource route (JSON API, no HTML)

New: loader.ts — You can now define loaders in a separate file instead of exporting from page.tsx. This keeps data fetching logic separate from your components:

app/dashboard/loader.ts
import type { LoaderContext } from '@paretojs/core'
export function loader(ctx: LoaderContext) {
return { stats: getDashboardStats() }
}

New: error.tsx — Create an error.tsx at the app root to customize the error page shown when a loader throws or a render error is not caught by a ParetoErrorBoundary. For component-level error isolation, use ParetoErrorBoundary anywhere in the component tree:

import { ParetoErrorBoundary } from '@paretojs/core'
<ParetoErrorBoundary fallback={({ error }) => <p>{error.message}</p>}>
<RiskyComponent />
</ParetoErrorBoundary>

defineStore() and defineContextStore() now use Immer for immutable state updates. Write mutations as if you’re mutating directly — Immer produces the immutable result:

import { defineStore } from '@paretojs/core/store'
const { useStore, getState, setState } = defineStore((set) => ({
count: 0,
increment: () =>
set((draft) => {
draft.count += 1 // Immer makes this immutable
}),
}))

The store API supports direct destructuring, automatic SSR serialization, and context-scoped stores for per-request isolation.

Pareto automatically applies OWASP-recommended security headers in development. For production, securityHeaders() is exported from @paretojs/core/node for use in custom server setups:

import { securityHeaders } from '@paretojs/core/node'
import express from 'express'
const app = express()
app.use(securityHeaders())

This sets X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Permissions-Policy, Strict-Transport-Security, and Cross-Origin-Opener-Policy headers automatically.

The CLI commands remain the same but are now built on cac instead of a custom parser:

Terminal window
pareto dev # Development server with HMR
pareto build # Production build (client + server + static)
pareto start # Start production server
  1. Update dependencies — Install @paretojs/core@3 and update to React 19.
  2. Remove Rspack config — Delete any custom Rspack configuration files. Create a standard vite.config.ts in your project root instead — Pareto loads and merges it automatically.
  3. Update error handlingerror.tsx is now optional and provides app-level error pages. Use ParetoErrorBoundary in your layouts/pages for component-level error isolation.
  4. Update imports — The @paretojs/core API surface is largely the same, but check that your imports match the API reference.
  5. Test your loaders — Loader behavior is unchanged, but verify that your data fetching works with Vite’s dev server.

Try it now:

Terminal window
npx create-pareto@latest my-app