Rebuilding the Obermind frontend: Vite, React 19 and one less server-rendered stack

Alexander Talei

Alexander Talei

Founder at Obermind

Rebuilding the Obermind frontend: Vite, React 19 and one less server-rendered stack

Obermind Platform 2026.11.3 is the first release on a rebuilt frontend.

The user app, the operator console and the server pages around them all changed in one week. This post covers what we replaced, why, and what we measured before and after.

Where we started

The user app was built on a toolchain its maintainers had deprecated. It still worked, but every upgrade had to go through it.

Three problems kept coming back:

  • Slow builds. A production build of the user app took 98 seconds.
  • A heavy first load. A signed-in user downloaded 1,682 KB (gzipped) before the home page could render.
  • A large dependency tree. The user app installed 1,829 packages, most of them build tooling we never shipped.

The operator console had its own older pipeline. It produced one 12.6 MB script, and a branch build spent 10 to 20 minutes on image optimisation, vendor copies and a second compile.

Around both apps sat a layer of server-rendered pages: sign-in, registration steps, account settings and old admin screens. They duplicated screens the apps already had.

What we changed

The work came in four steps, each released and measured separately.

  1. User app to Vite. We replaced the build toolchain and kept the application code.
  2. Lazy pages and a smaller shell. Each page became its own chunk, loaded only when the user can open it.
  3. React 19 and a new router. We moved the mobile UI layer two major versions forward, then moved to React 19.
  4. Retire the server-rendered pages. Sign-in, onboarding and settings now live in the user app. The operator console moved to a small dedicated build.

Builds: minutes to seconds

The user app's production bundle now builds in 6 to 10 seconds.

The full build, including a complete type check of the codebase, takes 48 seconds. That is half the old 98 seconds, which included a similar check.

The operator console changed more:

  • Before: a 10 to 20 minute branch build, producing one 12.6 MB script
  • After: about 25 seconds on a cold cache and about 8 seconds warm
  • Entry script: 669 KB (194 KB gzipped), down from 12.6 MB

The rest of the console is split by screen. Opening Users or Framework downloads that screen and nothing else.

The pipeline itself did not need rewriting. The console's build step kept its name, so the existing deployment job calls it unchanged.

What users download

On the user app, the first load of the home page fell from 1,682 KB to 889 KB gzipped. That is 47% less before the first screen renders.

Three changes produced most of that:

  • Pages load on demand. A page's code is requested only after the user is allowed to open it.
  • Vendor code is grouped by who uses it. The app shell no longer pulls in the data grid, the terminal's charting engine or the 3D globe.
  • One shared chart stack. We consolidated our charting code, which cut the home page's chart bundle by about 28%.

Tab buttons also prefetch their page when the pointer reaches them. By the time the click lands, the code is usually already there.

Faster at runtime

In a production build, signed in, against live prices:

  • Home → Trade: the new page is visible in about 0.10 seconds
  • Trade → Home: about 0.43 seconds
  • Idle on home with streaming prices: 120 fps, no long tasks
  • Memory across six screen changes: flat, between 199 and 221 MB

One finding was worth sharing. Rendering route changes as low-priority transitions looked like a free win.

In a trading app it was not. Every price update is urgent work, and each one restarted the transition. The old page stayed on screen for up to two seconds. We turned that behaviour off and recorded why in the code.

A smaller dependency tree

Fewer packages means faster installs, smaller CI caches and less to keep current.

  • User app: 1,829 → 846 installed packages (−54%)
  • Operator console: 1,734 → 467 installed packages (−73%)
  • Operator console, direct dependencies: 152 → 27

We also self-host the app fonts now. Neither app makes a third-party font request.

Retiring the server-rendered pages

We deleted 125 server-rendered views and the code behind them.

Sign-in, registration, KYC questionnaires, source of funds and account settings are now pages in the user app. They share its components, its theming and its white-label branding.

What remains on the server is small:

  • Two views: the shell that mounts the operator console
  • Redirects for links already sent in emails and push notifications
  • JSON and file endpoints, and provider callbacks

Links already sent in emails, push notifications and QR codes keep working. They redirect permanently to the new pages.

The same test cases run against the app's router and the server's redirect, so the two cannot drift apart.

Deployments and caching

Every file the build emits is content-hashed. The server sends those files with a one-year immutable cache header.

The app's entry page is never cached. A new deployment reaches users on their next load, while unchanged code is served from the browser cache.

The operator console's publish step now starts from a clean output folder. Stale files from earlier builds no longer ship, and a clean output is about 54 MB.

When a deployment replaces a page chunk while a user has the app open, the app notices. It reloads once to pick up the new version, with a guard so a broken deployment cannot cause a reload loop.

Compliance checks in one place

Every signed-in page in the user app goes through one gate: signed in, KYC loaded, KYC complete, verification done, area allowed. Only then is the page's code requested.

The route registry has no way to add a page without that gate. A new screen gets the checks by default.

Trade-offs

Not everything got smaller.

  • Sign-in pages are slightly heavier, 438 KB against 374 KB. The UI component layer is one chunk, which the app needs straight after sign-in anyway.
  • The browser floor moved. The app now needs Safari and iOS 16 or later, in the browser and in the native shell.
  • The dev server serves source files individually. A signed-in page loads hundreds of modules in development. Production is unaffected.

Audited after the refactor

Once the refactor was complete, we ran a full audit of the user app, the operator console and the server.

No exploitable vulnerabilities were found at any level.

Quality gates

The migration was done in small, reversible steps. Each step was checked in a production build before the next began.

  • 102 unit tests pass, including routing, the compliance gate and every legacy-link case
  • Lint: zero errors across the user app
  • 374 component props were updated by a syntax-tree codemod, not by hand
  • Device checks on the iOS and Android shells cover back navigation, deep links, safe areas and resume

What this means for licensees

Nothing changes in how you deploy. You take 2026.11.3 as a set, like any Obermind Platform release.

Your users get a faster first load and quicker navigation. Your operators get a lighter console. Your engineers get builds measured in seconds.

Get started

Read the 2026.11.3 changelog on your deployment, then talk to us about scheduling the upgrade.

Alipay
Stripe
Eurex
AWS
Azure
Google Cloud
Currenex
Velocity Trade
Bitcoin
CME
Interactive Brokers
Kraken
FIX API
XTX
Bloomberg
Binance

Запустите свою платформу.