Comments
Loading Translate Section...Loading Comment Form...
Loading Comment Form...
deleted
This website, 'Evame,' a multilingual community with automatic article and comment translation, migrated its meta-framework from Remix to Next.js, and its infrastructure from Render to Vercel.
It was a large-scale migration taking about two weeks, but I'm satisfied with the results. I've concluded that Remix/React Router is a great choice for small to medium-sized sites, while Next.js is highly valuable for larger ones.
This article details the migration from Remix to Next.js and offers a personal comparison of the two. If you're struggling to choose or considering a migration, please read on!
Website Policy Changes and Expansion of Scale
Initially, Evame started with the goal of providing translations for public domain texts like those from Aozora Bunko, Project Gutenberg, and old Buddhist scriptures. However, as development progressed, we wanted to extend translation capabilities to user-submitted articles as well. The fundamental purpose of Evame is the circulation of knowledge and stories; a system allowing user contributions is essential to this goal. This policy change led to increased site scale, new functional requirements, and a growing attraction to the robust ecosystem of Next.js.
Uncertainties Regarding the Future of Remix
On 2024/11/22, Remix v2 merged with React Router v7. https://remix.run/blog/react-router-v7
I thought Remix would disappear and continue as React Router, but the co-founder, also the author of the above blog post, stated on Twitter that the next version would be Remix v3.
I don't have any further plans to work on RR v7. I'm personally done with that project.
— MJ (@mjackson) December 21, 2024
Remix v3 is just something I've been calling it internally. Since we haven't released a v3 yet, it would make sense to publish the next version of Remix as v3.
Renaming a framework significantly impacts the entire ecosystem. In another project, updating from Remix v2 to React Router v7 rendered many Remix-related libraries unusable, requiring extensive adjustments and significant effort. The possibility of facing similar challenges with another renaming, potentially breaking many libraries again, was a major concern.
Tightening Integration Between React and Next.js
https://nextjs.org/blog/next-12
As noted in the above article, React and Next.js are increasingly intertwined, with Next.js pioneering many technologies ahead of other frameworks.
The fact that few frameworks still support RSC, despite its release in Next.js in 2023 (nearly two years ago), is evidence of this.
The ability to prioritize new feature implementation is a significant advantage.
Full Utilization of React 19
With Remix/React Router v7, data handling within route modules' loaders/actions often resulted in a separation between components and data. Next.js (App Router), using RSC (React Server Components), Server Actions, useActionState, etc., minimizes this distance, improving development productivity. The directory structure based on colocation is also easier to implement than in Remix, further boosting productivity.
Rich Ecosystem
Next.js boasts official examples for many services like Sentry, Supabase, and Stripe, and abundant supporting libraries. next/font and next/image are excellent, resulting in a perfect Lighthouse Performance and Best Practices score of 100.
Before the migration, the scores were lower, highlighting the significant impact of the ecosystem. (We'll work on Accessibility!)
AI-Friendly
When asking Claude or GPT to write code in Remix, it often generated Next.js code. The larger user base seems to increase the likelihood of Next.js producing usable code from the outset. This could be considered a new ecosystem for the AI era.
Slow Build Times
Remix's HMR is fast, providing a very comfortable local development experience. Next.js, however, can take several seconds to build certain files, sometimes requiring a manual reload, resulting in a frustrating development experience.
Complex Mental Model
Remix's mental model is simple and clear: loader → component → action.
Next.js introduces various concepts: RSC/Server Actions/revalidatePath/revalidateTag/next/dynamic/Request Memoization. While optimization improved Lighthouse scores, the perceived speed wasn't noticeably different from Remix. As a user, I just want it to be fast; I don't care how it's achieved.
Dark Magic
Remix generates very clean HTML; what you write is what you get, making it easy to understand.
Next.js inserts framework-specific logic (e.g., className, canvas tags), complicating debugging. Issues where code runs locally but fails in production, often involving Edge Runtime, are common.
I started by creating a new branch with `create next` and began migrating libraries. This included the following:
remix-auth → auth.js
react-i18next → next-intl
The change to remix-auth → auth.js required adjustments to the DB schema and Prisma (our ORM) to support Edge Runtime. This was the most challenging part of the entire migration.
For the DB schema, I prepared a schema matching auth.js, migrated existing data, and minimized customizations to reduce upgrade risks.
For Edge Runtime, Prisma Accelerate lacked sufficient examples, and the potential for significant differences between local and production environments was concerning. While migrating to Drizzle offered high flexibility, it would have been more time-consuming. As a result, I opted for Neon + Prisma, keeping the effort to a minimum.
Next, I replaced existing Remix routing and loader/action components with Next.js App Router RSC/Server Actions. AI was extremely helpful here. However, useActionState and Server Actions still require manual coding.
Avoiding Shared Logic in Server Actions
Since all Server Actions are publicly accessible, authentication code is needed for each. Consider using functions or middleware to consolidate authentication logic where possible.
Testing RSC Components
Testing methodologies for RSC are still under development. Separating business and authentication logic makes unit testing easier.
useActionState Typing
A mechanism for adding type information to the return value of useActionState isn't fully established. Defining types manually is currently the better approach. I use the following method (inspired by @about_hiroppy's tweet).
export type ActionResponse<T = void, U = Record<string, unknown>> = {
success: boolean;
message?: string;
data?: T;
zodErrors?: typeToFlattenedError<U>["fieldErrors"];
};
Form Validation
It's best to perform validation within Server Actions. I personally use zod as follows:
const editPageContentSchema = z.object({
slug: z.string().min(1),
title: z.string().min(1).max(100),
pageContent: z.string().min(1),
});
const parsedFormData = editPageContentSchema.safeParse({
slug: formData.get("slug"),
title: formData.get("title"),
pageContent: formData.get("pageContent"),
});
Small to Medium-Sized Apps
If you don't need to utilize the latest React features extensively, Remix/React Router offers a simple and fast development experience. The mental model is also straightforward.
For B2B or SPAs, I personally recommend Remix/React Router.
Medium to Large-Scale or Those Wishing to Leverage the Ecosystem
Next.js facilitates integration with services and libraries and provides early access to future React updates.
For B2C or SSR, choose Next.js.
I believe that migrating to Next.js after initial release and growth is a perfectly viable approach. Remix/React Router with Cloudflare keeps costs down. However, migrations are time-consuming, so choosing Next.js from the start is a strong option if you anticipate future expansion.
The migration of the framework, database, and infrastructure was extensive. However, having test code for critical parts and separating DB code using CQRS resulted in a relatively smooth process, except for authentication. I've reaffirmed the importance of investing in code quality and architecture.
Evame is a multilingual community where AI automatically translates articles and comments. This migration allowed me to write this migration report in Japanese and share it with an English-speaking audience.
If you're interested, please write an article in your preferred language and share it with the world!
These are my thoughts, comparisons, and migration steps from Remix/React Router v7 to Next.js. I hope this is helpful!