Skip to content

♻️(front) WIP test Vite+Tanstack Router instead of Next.js#675

Open
sylvinus wants to merge 3 commits into
mainfrom
vite
Open

♻️(front) WIP test Vite+Tanstack Router instead of Next.js#675
sylvinus wants to merge 3 commits into
mainfrom
vite

Conversation

@sylvinus
Copy link
Copy Markdown
Member

@sylvinus sylvinus commented May 26, 2026

Too many CVEs on Next.js lately, this is just to evaluate how we'd migrate off it.

Summary by CodeRabbit

  • New Features

    • Frontend migrated to Vite + TanStack Router with rewritten file-based routes for mailbox/thread pages, message flows, integrations, signatures, templates, and an updated app entrypoint.
    • Added a bundle analysis make target.
  • Documentation

    • README and frontend docs updated for Vite/TanStack Router and new developer scripts.
  • Bug Fixes / Chores

    • Updated environment defaults (local API origin) and PWA HTML entry.
    • Updated ignored build artifacts and Docker build output layout.
  • Refactor

    • Routing/SDK and environment access switched to Vite-style APIs; Sentry and widget configs now read from Vite env.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 26, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4ba891b2-dcde-4c46-92ac-ef62953f85d7

📥 Commits

Reviewing files that changed from the base of the PR and between 44988d2 and 40d1654.

📒 Files selected for processing (2)
  • .gitignore
  • src/frontend/src/lib/router-compat.ts

📝 Walkthrough

Walkthrough

Migrates the frontend from Next.js to Vite and TanStack Router: adds Vite tooling and config, creates a Vite HTML entry and main.tsx bootstrap, converts Pages routes into TanStack file routes, updates components to use a router-compat layer, switches client env reads to import.meta.env, and updates infra/config files.

Changes

Frontend Migration to Vite and TanStack Router

Layer / File(s) Summary
Vite build tooling and configuration
src/frontend/vite.config.ts, src/frontend/tsconfig.json, src/frontend/package.json, Makefile
Vite config added (plugins, envPrefix, alias, server/preview), tsconfig adjusted for Vite (ES2020, vite/client types), and package.json scripts/deps switched from Next.js to Vite/TanStack tooling; new analyze-front Make target added.
HTML entry point and React app bootstrap
src/frontend/index.html, src/frontend/src/main.tsx, src/frontend/src/routes/__root.tsx, src/frontend/src/vite-env.d.ts
Adds index.html mount and PWA meta, main.tsx bootstraps i18n, instrumentation and mounts TanStack Router; __root.tsx provides QueryClient, global error handling, theme favicon injection, and provider composition; Vite env typings added.
Routes conversion (domain, mailbox, home)
src/frontend/src/routes/...
Converts domain and mailbox pages from Next.js pages to TanStack file routes (createFileRoute), adds mailbox/domain subroutes (autoreplies, signatures, integrations, message-templates, new, thread), and wires MainLayout/thread providers where required.
Router compatibility shim
src/frontend/src/lib/router-compat.ts
Adds a Next-like router compatibility layer (useRouter, router facade, usePathname, useSearchParams, useParams, Link, ErrorPage) backed by TanStack Router for callers expecting Next.js primitives.
Component routing hooks migration
src/frontend/src/features/**, src/frontend/src/hooks/*
Systematically updates imports to use @/lib/router-compat (useRouter, useSearchParams, Link, useParams, usePathname) across many components and hooks; replaces Next.js Image usage with plain img where applicable.
Sentry and client env migration
src/frontend/instrumentation-client.ts, src/frontend/src/features/utils/errors/index.ts, src/frontend/src/features/**
Switches Sentry usage to @sentry/react and replaces process.env.NEXT_PUBLIC_* reads with import.meta.env.NEXT_PUBLIC_* across instrumentation, error handling, widgets, i18n, theme, API origin, and other client config sites.
Infra, styles and lint
src/frontend/caddy/Caddyfile, src/frontend/Dockerfile, src/frontend/eslint.config.mjs, src/frontend/src/styles/*, .gitignore, src/frontend/README.md
Caddy changed to SPA fallback and error handling, Dockerfile copies dist not out, ESLint config updated for Vite/TS, SCSS import paths moved to views, gitignores updated for dist and routeTree/gen, and README rewritten for Vite/TanStack Router.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly indicates the main change: migrating the frontend from Next.js to Vite and TanStack Router. Though it includes 'WIP' and uses an emoji prefix, it accurately summarizes the primary objective.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/frontend/caddy/Caddyfile`:
- Around line 38-46: The current handle_errors block rewrites every error
(rewrite * /index.html) causing non-404 errors to return the SPA HTML; change
the handle_errors behavior to only rewrite and serve /index.html for 404s by
adding an error matcher (e.g., `@spa_404` using expression {err.status_code} ==
404) and move the rewrite/file_server into a handle `@spa_404` { ... } block so
other errors fall through to Caddy’s default handling; update the existing
handle_errors block that contains rewrite * /index.html and file_server
accordingly and keep the SPA try_files {path} /index.html behavior unchanged.

In `@src/frontend/eslint.config.mjs`:
- Around line 16-17: Update the ESLint rules in src/frontend/eslint.config.mjs:
change the rule values for "react-hooks/exhaustive-deps" and "react-hooks/refs"
from "off" to "warn" so React Hooks correctness checks run as warnings instead
of being disabled; locate the rules in the exported config object and replace
their string values accordingly.

In `@src/frontend/package.json`:
- Around line 37-38: Update the TanStack router-related dependencies so their
versions align with `@tanstack/react-router`@1.170.8: change
"`@tanstack/react-router-devtools`" from 1.167.0 to 1.170.8 and change
"`@tanstack/router-plugin`" (if present) to 1.170.8 as well; ensure package.json
entries for "`@tanstack/react-router`", "`@tanstack/react-router-devtools`", and
"`@tanstack/router-plugin`" all use the same 1.170.8 version, then run npm/yarn
install and verify no peer dependency warnings remain.

In `@src/frontend/README.md`:
- Line 1: Add a top-level (level-1) heading as the very first line of
src/frontend/README.md to satisfy MD041: insert a line like "# Messages" (or
another appropriate H1) above the existing first line "This is the Messages
frontend, built with [Vite] and [TanStack Router]." so the file's first line is
a heading.

In `@src/frontend/src/routes/__root.tsx`:
- Line 50: The THEME constant initialization uses JSON.parse on
process.env.NEXT_PUBLIC_THEME_CONFIG which can throw and crash the app during
module init; change it to safely parse with a try/catch (or a small safeParse
helper) around JSON.parse(process.env.NEXT_PUBLIC_THEME_CONFIG) and fall back to
a default object/string (e.g., "white-label") if parsing fails or the env var is
missing, ensuring THEME is assigned a robust value; update the reference where
THEME is declared so it never throws during module load.
- Around line 79-80: Wrap the unconditional devtools render in a dev-only guard:
only render <ReactQueryDevtools ... /> and <TanStackRouterDevtools ... /> when
the app is running in development (e.g. check import.meta.env.DEV or
process.env.NODE_ENV !== 'production') inside
src/frontend/src/routes/__root.tsx; keep the existing props (initialIsOpen and
buttonPosition) and simply conditionally render those two components so they are
excluded from production bundles.

In `@src/frontend/src/routes/mailbox/`$mailboxId/autoreplies/index.tsx:
- Line 26: The page root div with className "admin-page" is missing the shared
skip-link target id used by sibling mailbox settings routes; locate the root
element (the div with className "admin-page") in autoreplies/index.tsx and add
the same skip-link target id string used elsewhere (the id used by the other
mailbox settings pages) so keyboard users can use consistent skip navigation
across mailbox routes.

In `@src/frontend/src/routes/mailbox/`$mailboxId/integrations/index.tsx:
- Around line 35-46: The page renders header/actions even when selectedMailbox
is not yet available; add an early guard that returns nothing (or the same
empty/redirect used by sibling mailbox settings) when selectedMailbox is falsy
to avoid transient UI. Specifically, in the component containing
selectedMailbox, add an early check like "if (!selectedMailbox) return null;"
(or the project-standard empty/redirect) before the JSX that currently renders
<div className="admin-page" ...> so the header, <CreateIntegrationAction />, and
<IntegrationsPageContent /> are only rendered when selectedMailbox is present.

In `@src/frontend/vite.config.ts`:
- Around line 52-55: The build currently disables sourcemaps (build.sourcemap:
false) which prevents Sentry symbolication; change the Vite config
(build.sourcemap) to generate production sourcemaps (e.g., "hidden" or true) and
ensure the Sentry initialization (Sentry.init) is wired with matching
release/dist identifiers so artifact association works; also add or document a
CI step to upload those sourcemaps (e.g., via sentry-cli upload-sourcemaps)
using the same release/dist values so stack traces are symbolicated in
production.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: f07d48cc-d5ed-4115-8fd4-9dd8b588513b

📥 Commits

Reviewing files that changed from the base of the PR and between e6bf029 and 7f9f198.

⛔ Files ignored due to path filters (1)
  • src/frontend/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (73)
  • .gitignore
  • Makefile
  • README.md
  • env.d/development/frontend.defaults
  • src/frontend/.gitignore
  • src/frontend/Dockerfile
  • src/frontend/README.md
  • src/frontend/caddy/Caddyfile
  • src/frontend/eslint.config.mjs
  • src/frontend/index.html
  • src/frontend/instrumentation-client.ts
  • src/frontend/next.config.ts
  • src/frontend/package.json
  • src/frontend/scripts/print-bundle-stats.mjs
  • src/frontend/src/features/controlled-modals/message-importer/step-form.tsx
  • src/frontend/src/features/forms/components/message-form/index.tsx
  • src/frontend/src/features/forms/components/search-input/index.tsx
  • src/frontend/src/features/layouts/components/admin/admin-layout.tsx
  • src/frontend/src/features/layouts/components/admin/modal-create-update-mailbox/index.tsx
  • src/frontend/src/features/layouts/components/mailbox-panel/components/mailbox-actions/index.tsx
  • src/frontend/src/features/layouts/components/mailbox-panel/components/mailbox-labels/components/label-form-modal/index.tsx
  • src/frontend/src/features/layouts/components/mailbox-panel/components/mailbox-labels/components/label-item/index.tsx
  • src/frontend/src/features/layouts/components/mailbox-panel/components/mailbox-list/index.tsx
  • src/frontend/src/features/layouts/components/mailbox-panel/index.tsx
  • src/frontend/src/features/layouts/components/main/authenticated-view.tsx
  • src/frontend/src/features/layouts/components/main/header/authenticated.tsx
  • src/frontend/src/features/layouts/components/main/index.tsx
  • src/frontend/src/features/layouts/components/thread-panel/components/thread-item/index.tsx
  • src/frontend/src/features/layouts/components/thread-panel/components/thread-panel-header.tsx
  • src/frontend/src/features/layouts/components/thread-panel/hooks/use-thread-panel-filters.ts
  • src/frontend/src/features/layouts/components/thread-panel/index.tsx
  • src/frontend/src/features/layouts/components/thread-selection-placeholder.tsx
  • src/frontend/src/features/layouts/components/thread-view/components/thread-summary/index.tsx
  • src/frontend/src/features/message/use-delete-label.tsx
  • src/frontend/src/features/message/use-split-thread.tsx
  • src/frontend/src/features/providers/admin-maildomain.tsx
  • src/frontend/src/features/providers/mailbox.tsx
  • src/frontend/src/features/providers/modal-store/index.tsx
  • src/frontend/src/features/providers/thread-selection.tsx
  • src/frontend/src/features/ui/components/label-badge/index.tsx
  • src/frontend/src/features/utils/errors/index.ts
  • src/frontend/src/hooks/use-safe-router-push.ts
  • src/frontend/src/main.tsx
  • src/frontend/src/pages/_app.tsx
  • src/frontend/src/pages/_document.tsx
  • src/frontend/src/pages/domain/[maildomainId]/signatures.tsx
  • src/frontend/src/pages/mailbox/[mailboxId]/autoreplies/index.tsx
  • src/frontend/src/pages/mailbox/[mailboxId]/index.tsx
  • src/frontend/src/pages/mailbox/[mailboxId]/integrations/index.tsx
  • src/frontend/src/pages/mailbox/[mailboxId]/message-templates/index.tsx
  • src/frontend/src/pages/mailbox/[mailboxId]/new/index.tsx
  • src/frontend/src/pages/mailbox/[mailboxId]/signatures/index.tsx
  • src/frontend/src/pages/mailbox/[mailboxId]/thread/[threadId].tsx
  • src/frontend/src/routes/__root.tsx
  • src/frontend/src/routes/domain/$maildomainId/dns.tsx
  • src/frontend/src/routes/domain/$maildomainId/index.tsx
  • src/frontend/src/routes/domain/$maildomainId/signatures.tsx
  • src/frontend/src/routes/domain/index.tsx
  • src/frontend/src/routes/index.tsx
  • src/frontend/src/routes/mailbox/$mailboxId/autoreplies/index.tsx
  • src/frontend/src/routes/mailbox/$mailboxId/index.tsx
  • src/frontend/src/routes/mailbox/$mailboxId/integrations/index.tsx
  • src/frontend/src/routes/mailbox/$mailboxId/message-templates/index.tsx
  • src/frontend/src/routes/mailbox/$mailboxId/new/index.tsx
  • src/frontend/src/routes/mailbox/$mailboxId/signatures/index.tsx
  • src/frontend/src/routes/mailbox/$mailboxId/thread/$threadId.tsx
  • src/frontend/src/styles/main.scss
  • src/frontend/src/styles/views/_index.scss
  • src/frontend/src/styles/views/domain/_index.scss
  • src/frontend/src/styles/views/mailbox/_index.scss
  • src/frontend/src/styles/views/mailbox/new/_index.scss
  • src/frontend/tsconfig.json
  • src/frontend/vite.config.ts
💤 Files with no reviewable changes (12)
  • src/frontend/src/pages/mailbox/[mailboxId]/index.tsx
  • src/frontend/src/pages/mailbox/[mailboxId]/autoreplies/index.tsx
  • src/frontend/src/pages/domain/[maildomainId]/signatures.tsx
  • src/frontend/src/pages/mailbox/[mailboxId]/signatures/index.tsx
  • src/frontend/src/pages/mailbox/[mailboxId]/integrations/index.tsx
  • src/frontend/src/pages/mailbox/[mailboxId]/message-templates/index.tsx
  • src/frontend/src/pages/mailbox/[mailboxId]/thread/[threadId].tsx
  • src/frontend/src/pages/_document.tsx
  • src/frontend/src/pages/_app.tsx
  • src/frontend/next.config.ts
  • env.d/development/frontend.defaults
  • src/frontend/src/pages/mailbox/[mailboxId]/new/index.tsx

Comment thread src/frontend/caddy/Caddyfile Outdated
Comment thread src/frontend/eslint.config.mjs
Comment thread src/frontend/package.json
Comment thread src/frontend/README.md
Comment thread src/frontend/src/routes/__root.tsx Outdated
Comment thread src/frontend/src/routes/__root.tsx Outdated
Comment thread src/frontend/src/routes/mailbox/$mailboxId/autoreplies/index.tsx Outdated
Comment thread src/frontend/src/routes/mailbox/$mailboxId/integrations/index.tsx
Comment thread src/frontend/vite.config.ts Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/frontend/vite.config.ts`:
- Around line 40-42: The envPrefix in vite.config.ts currently uses the
Next.js-specific string 'NEXT_PUBLIC_' which can be confusing post-migration;
change envPrefix to a framework-agnostic value (e.g., 'PUBLIC_' or 'VITE_') by
updating the envPrefix property in vite.config.ts and then rename all usages of
import.meta.env.NEXT_PUBLIC_* to the new prefix (e.g., import.meta.env.PUBLIC_*
or import.meta.env.VITE_*), and update any docs, tests, CI config, and .env
files that reference NEXT_PUBLIC_ so everything remains consistent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3b3e262a-8565-4fb2-b718-ca9510b3c9a0

📥 Commits

Reviewing files that changed from the base of the PR and between 7f9f198 and 44988d2.

📒 Files selected for processing (18)
  • src/frontend/README.md
  • src/frontend/caddy/Caddyfile
  • src/frontend/instrumentation-client.ts
  • src/frontend/src/features/api/utils.ts
  • src/frontend/src/features/controlled-modals/message-importer/use-bucket-upload.tsx
  • src/frontend/src/features/i18n/conf.ts
  • src/frontend/src/features/layouts/components/mailbox-settings/modal-compose-integration/widget-integration-form.tsx
  • src/frontend/src/features/providers/theme.tsx
  • src/frontend/src/features/ui/components/feedback-button/index.tsx
  • src/frontend/src/features/ui/components/feedback-widget/index.tsx
  • src/frontend/src/features/ui/components/lagaufre/index.tsx
  • src/frontend/src/features/utils/errors/index.ts
  • src/frontend/src/routes/__root.tsx
  • src/frontend/src/routes/mailbox/$mailboxId/autoreplies/index.tsx
  • src/frontend/src/routes/mailbox/$mailboxId/integrations/index.tsx
  • src/frontend/src/vite-env.d.ts
  • src/frontend/vite.config.ts
  • src/frontend/vitest.config.ts

Comment on lines +40 to +42
// App env vars are read via `import.meta.env.NEXT_PUBLIC_*`. envPrefix
// tells Vite which env vars to expose to client code at build time.
envPrefix: 'NEXT_PUBLIC_',
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial | 💤 Low value

Consider renaming the environment variable prefix post-migration.

envPrefix: 'NEXT_PUBLIC_' retains the Next.js naming convention after migrating to Vite. While functionally correct, this may confuse future developers. Consider renaming to a framework-agnostic prefix like PUBLIC_ or VITE_ in a follow-up refactor to avoid coupling the codebase to Next.js naming.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/frontend/vite.config.ts` around lines 40 - 42, The envPrefix in
vite.config.ts currently uses the Next.js-specific string 'NEXT_PUBLIC_' which
can be confusing post-migration; change envPrefix to a framework-agnostic value
(e.g., 'PUBLIC_' or 'VITE_') by updating the envPrefix property in
vite.config.ts and then rename all usages of import.meta.env.NEXT_PUBLIC_* to
the new prefix (e.g., import.meta.env.PUBLIC_* or import.meta.env.VITE_*), and
update any docs, tests, CI config, and .env files that reference NEXT_PUBLIC_ so
everything remains consistent.

@jbpenrath jbpenrath self-assigned this May 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants