page actually took 17s to fully render with multiple shift changes.
all to render a domain search bar similar to google home page.
For the curious, google's current homepage is a 200kb payload all in, or about 50 times smaller.
But seriously, not sure why NextJS builds take so much, we are using stable and functional pages router in DollarDeploy and it is still takes too much time to build.
*I know this is just build time, so this is different then their deployement time
Is the quality of software engineers really dropped that low that people get excited when they move off from "heavy bloated" frameworks to lighter alternatives? Or is this just SEO farming garbage to position the company higher in search results?
Next.js is produced by Vercel, a competitor to Railway.
I run a Next.js App Router site in production (marketing + blog). Build times aren't painful yet, but I've noticed the same pattern: most of the build time is Next.js doing things I didn't ask for. For a mostly-static marketing site it's tolerable, but I can see how it becomes a dealbreaker for a rich client-side app like Railway's dashboard.
Curious — after the migration, did you see any measurable difference in runtime performance (TTFB, hydration) or was the win purely on the build/DX side?
I did an optimization pass for a client once where I got rid of a ton of the sprites but didn't have the energy to redo it all, so it just had huge sections that were blank.
Super snappy loading afterwards though.
All my projects are server rendered with jinja/minijinja, bootstrap, jQuery, and htmx when I need a little bit of SPA behavior on forms.
No builds, just static <script src= tags. Very fast and easy. I'll never recommend anything else.
Aside from some difficulty with mastering environment variables, I’ve been delighted with the change and will probably not look back.
Looks like maybe things haven't changed much?
It provides such things as:
```
import { Debouncer } from '@tanstack/pacer' // class
const debouncer = new Debouncer(fn, options)
debouncer.maybeExecute(args) // execute the debounced function
debouncer.cancel() // cancel the debounced function
debouncer.flush() // flush the debounced function
```
Why? Just why do you need to install some "framwork" for implement debouncing? Isn't this sort of absurdism the reason why the node ecosystem is so insecure and vulnerable in the first place? Just write a simple debouncer using vanilla js...
Then you can even run multiple projects off the same server.
Many of my customers insists on using Next.js or similar but when I browse their website I don't get the point. They are downloading and executing megabytes of JS while in-page interactions tends to be limited to few basic stuff. Never seen one of their project requiring offline mode. Maybe that's being able to easily replace a [FRAMEWORK] dev with another.
TanStack just has a nicer mental model overall and works great with TanStack query for cache I validation and stuff like that.
Remix was promising but there was so much ceremony in registering API routes and stuff. Tanstack just lets you define server functions arbitrarily with no ceremony.
Might be worth a spike and some tokens to ask Claude Code to migrate and test the build time and ergonomics.
On hobby projects same script approach without any kind of build step.
Now it doesn't really impact build time and Railway offers Next.js hosting.
At DollarDeploy we building it also in containers but every build get 4GB/2CPU so it is quite fast but not as fast as Vercel.
We also recently cut our build times in half moving from Webpack to Turbopack on production builds after jumping to NextJS 16. We'd already been using Turbopack in development for a while which yielded massive DX improvements related to performance. Production build times will drop further once Turbopack production build caching is stable.
Webpack -> Turbopack is the smart initial migration. I'd bet Railway went straight from Webpack -> Vite not realising that their real gains sat with the build tooling, not NextJS vs Tanstack.
I also have experimented with HTMX and Django, and that seems to be a nice combination.
Everything is AJAX again.
If web interface is an application backed by a remote state HTMX falls apart.
CSS is a total mess. HTML is a mess. JS is okay, but is not a high quality language.
We would save so much time and money if we would have a modern base to build on. Sadly this will probably never happen, because company interests will try to corrupt the process and therefore destroy it.
I've seen vanilla JS before, and I just know I wouldn't want to do the housekeeping that comes with it. People claim it's less work because it' simpler, but I fully expect myself to rewrite the thing at least twice, only to give up because I have no actual mental model anymore of how it works.
Makes me think that there’s no way this is computationally efficient either.
It's way way way way easier managing a basic VPS that can be highly performant for your needs. If this was 2010, I'd agree with you but tooling and practices have gotten so much better over the last decade (especially the last 5 years).
There might be more irony in saying it's stylized pixels without realizing that the style of the image can't be replicated with blocks of the same size but I dunno, I'm not Alanis Morissette
There are many conditions under which the hot reload just straight up crashes out regularly.
One way to think about it might be that the site supports lots of users who use it for various things. So, everyone uses 80% of the site, but everyone also uses a different portion of the final 20%. So, if you have lots of users, you might also have lots of smaller features that a significant minority use.
I don't know, just an interesting way of thinking about it.
[0]: https://nextjs.org/docs/app/guides/ai-agents#how-it-works
Anyway, my point is that no one is forced to use NextJS and if they like NextJS but not Vercel they can always fork it or, apparently write an adapter.
Railway's entire production frontend no longer runs on Next.js. The dashboard, the canvas, railway.com, all of it now runs on Vite + TanStack Router, and we shipped the migration in two PRs with zero downtime.
Next.js got railway.com from zero to a production app serving millions of users monthly. It's an excellent framework, but it stopped being the right one for our product.
Frontend builds had crept past 10 minutes. Six of those minutes were Next.js alone, half of it stuck on "finalizing page optimization." For a team that ships multiple times a day, that kind of build time isn't a minor annoyance. It's like a very expensive tax on every single iteration.
Railway’s app is overwhelmingly client-side. The dashboard is a rich, stateful interface. The canvas is real-time. Websockets are everywhere. The server-first primitives in Next.js weren't something we used, and we'd ended up building our own abstractions on top of the Pages Router just to support layouts and routing concerns that the framework didn't handle the way we needed.
We were still on the Pages Router, which made shared layouts hacky. Every layout pattern was a bolted-on workaround rather than a first-class framework primitive. The App Router would have solved some of these problems, but it leans heavily into server-first patterns, and our product is intentionally client-driven. Adopting it would have meant rebuilding around a paradigm we don't need.
We wanted a stack that matches how we actually build: explicit, client-first, and fast to iterate on. It also helps that we genuinely enjoy working with it.
For the Product team, we wanted a few niceties that help us avoid thinking about how we needed to implement our front-end and found the following to really convince us.
Several of us tried TanStack Start over the holidays and the reaction was unanimous. We like building with it, and for a product like Railway's dashboard, that matters as much as any benchmark.
Once we made the choice, I got to work. Pre-squash before merge, I must have made 100s of commits.
Migrating a production frontend that serves millions of users across 200+ routes is the kind of thing that usually takes months of parallel running and incremental cutover. We were on a deadline, so I did it in two pull requests.
PR 1 replaced everything Next.js-specific: next/image, next/head, next/router. Each was swapped for either a native browser API or a framework-agnostic alternative. This PR changed nothing about the framework itself. It just removed every dependency on it, so that PR 2 could be a clean swap.
PR 2 swapped the framework. 200+ routes migrated. We systematically extracted everything non-routing-related from page files into individual React components first, then generated all routes from the original page tree.
We then added Nitro as the server layer and replaced next.config.js with Nitro config, consolidating redirects (500+), security headers, and caching rules into one place. We also replaced Node.js APIs that Next.js had provided polyfills for (Buffer, url.parse, and others) with browser-native alternatives, which left us with cleaner code as a side effect.
Merged on an early Sunday morning. The team dogfooded immediately with a live war room in Discord, and a stream of fixes landed same day. No downtime.
Sure we gained a faster, more explicit stack, but not without trade-offs.
next/image with <img> tags and Fastly image optimization at the edge.next-seo and next-sitemap with small in-house equivalents. Straightforward to build, no extra dependencies.We run our production frontend the same way our users run theirs: preview deploys per PR, health checks, zero-downtime rollouts. When we swapped the entire build system and framework, we didn't touch infrastructure. We changed code, pushed it, and Railway handled the rest.
Fastly now serves most of our traffic directly from the edge. Marketing pages are cached, dynamic pages use ISR where needed, and our frontend servers are mostly idle as a result. Vite's asset model makes this work particularly well. Each module gets its own content-hashed chunk, so shipping a change to billing only invalidates that chunk. Returning users download kilobytes, not megabytes.
This is how we think frontends should be deployed: the build is fast, the assets are immutable and cache-friendly, and the infrastructure underneath handles rollouts, previews, and routing without you having to think about it. Your frontend framework should be optimized for iteration speed, and your infrastructure should make shipping those iterations invisible. That's the experience we're building for ourselves and for everyone on Railway.
The speed of iteration on a frontend matters more now than it ever has.
Builds that took 10+ minutes now finish in under two. The dev server starts instantly. Route changes are type-checked at the boundary. Layouts compose without workarounds.
The gap between writing code and getting it in front of users is the bottleneck, and everything we've done here, the framework swap, the edge caching, the asset model, is about closing that gap. Vite + TanStack sets us up for a world where shipping frontend changes is near-instant, and that's the world we're building toward.
Same for HTML. If the web would be reimagined today, there is a very low chance that we would create HTML as is.
I can see though that today's AI models could eventually replace the human in the loop and truly automatically fix every possible situation.
second biggest problem is "no stricter mode". so even wrong or useless html/css code goes unflagged and is treated as it is normal.
CSS is way too powerful.
At times I would see people daily asking for help with their broken Tailwind setups, and almost always it was them trying to use Tailwind v4 the v3 way because some AI told them so.
You get to pick Vercel + headless CMS + assets managed + eshop, and you're done in terms of big corporations.
Might seem a lot in licenses, however it allows for smaller dev teams, which is what management floor cares about, all those salaries.
Note how many HNers are making the same remark.
https://csszengarden.com/pages/alldesigns/
That statement wasn't true ages ago, and it's even less true now.