7 ms·
One year with Next.js App Router and why we're moving on
- deleted 11mo ago[deleted]
- mayaj47 11mo agohave you tried Nuxt.js
- skeptrune 11mo ago>This is because when Next.js loads the actual Server Component, no matter what, the entire page re-mounts. I was begging Next.js to just update the existing DOM and preserve state, but it just doesn't. YES! YES! I FEEL SO SEEN RIGHT NOW! I find this behavior unbelievably frustrating. It's hard for me to understand why they ever even shipped RSC's without fixing this.
- sibeliuss 11mo agoOur experience entirely. We replaced next.js with a simple router and everything in every sense got simpler, and FASTER. It was a remarkable education, replacing that crazy thing.
- 383toast 11mo agoyeah RSC is totally unnecessary it turns out
- terandle 11mo agoIt's a good idea in theory, the perf just needs to be better. Maybe with bun.
- BoorishBears 11mo ago"1 more step function in performance bro, V8 was cool but just 1 more and we'll have enough to make CRUD apps in JS, bro I promise" Or you can use React Query/Tanstack Query, not waste cycles and bandwidth on RSC, get an app with better UX (http://ilovessr.com http://ilovessr.com), and a simpler mental model that's easier to maintain.
- terandle 11mo agoYeah Vite+Reat+Tanstack SPA apps is definitely the way to go for a majority of web apps. I would still stick with nextjs for ecommerce or pages that need to load instantly when clicked from google however.
- nicce 11mo agoBun unfortunately isn’t production ready for years for any serious application. Too many security problems.
- atonse 11mo agoReally? Do you have links to any good analysis on this? I'd be shocked, given that the bun team has shown a ton of maturity in all their messaging as far as API compatibility, engineering chops, and attention to detail. Nothing I've seen suggests that they'd be sloppy on the security side.
- 383toast 11mo ago...or just not use server components :)
- 383toast 11mo agoand watch your code be 10x easier to reason about.
- 383toast 11mo ago... oh wait that's what the author ended up doing LOL
- hdjrudni 11mo agoI'm surprised so many people drank the RSC koolaid. I tried it for maybe an hour and it became painfully obvious very quickly how much harder it is to build something that used to be simple. I just don't understand the use-case either. Either you're building an SEO-optimized website and you want that initial page load to be as fast as possible. In this case, just build a static website. Use whatever technology you desire and compile to HTML+CSS. Or you're building an "app" in which case you should expect users to linger around for a bit and that fat initial payload will eventually be cached, so you really don't need to sending it down on every click. So go full-on with the client-side rendering and simplify your stack a little. You can still do a lot of optimizations like code-splitting and prefetching and this and that, but we don't need this weird mixed modality where some things work in one place but not the other. Which is pretty much what the author says and I'm glad to see people start to realize this.
- culi 11mo ago> Or you're building an "app" [...] So go full-on with the client-side rendering I wish companies would take this a step further still and just build a PWA. This gives you access to so many web APIs that can further simplify your stack. I agree that it's bewildering to see how many companies reach for Nextjs for webapps that don't need SEO optimization but some of the more complex rendering strategies can still be useful for web apps as well. Even for PWAs
- PebblesHD 11mo agoI have yet to reach the limits of doing a Vite create and installing react router myself for the several entirely client side apps we manage. It has sane build defaults and for whatever definition of ‘works’ is possible in JS, ‘just works’. If it becomes too complex for that basic setup it usually means we’ve over-complicated something. Where we have a need for server side, nodejs just never felt natural for us so we stuck with java springboot or flask/fastapi as appropriate.
- 383toast 11mo agoever since react router got merged with remix to become react router v7, I looked around for simpler version, landed on Wouter which is fine.
- davedx 11mo agoI’ve been using Wouter on multiple medium sized projects for 3-4 years now. I’m never going back to react-router if I can avoid it: a hellhole of API churn and self promotion
- petralithic 11mo agoTanStack is the sane option here, whether their router or their start product.
- culi 11mo agoI'd love to hear more about what motivated the switch. All the additions to react-router are, afaict, opt-in. React-router has 3 "modes"[0] and the declarative mode seems pretty much exactly what the classic library is like with some extra components/features you don't have to use Thought I've enjoyed the code-splitting and access to SPA/SSR/SSG/etc strategies that come with the "framework" mode [0] https://reactrouter.com/start/modes https://reactrouter.com/start/modes
- PebblesHD 11mo agoYep, declarative mode is what I use, tried the data mode with loaders and it never really agreed with me as a pattern.
- zenethian 11mo agoIt feels like so much work has been done to just end up going full circle back to Django-style website applications. All of these frameworks have continually re-solved problems that were already solved in something other than Javascript, and then people write blogs about how they're surprised about it. It feels a bit uncanny to see.
- aaronbrethorst 11mo agoPromo packets are a hell of a drug
- pjmlp 11mo agoI am happy to have stayed mostly in Java and .NET land all these two decades, although I do have to put with Next.js for the last three years as well.
- benoau 11mo agoThese days I get a lot of deju vu with ASP.NET's web forms from 20 years ago.
- figassis 11mo agoAround 8y ago, when Angular vs React was still a war worth reading, frameworks were I think in their final state. They gave you basic tools, and you could build applications with them. I felt like framework creators didn’t treat us like babies who needed handholding. Idk if a new generation of younger developers took over, but things started becoming too shiny. Blog posts were no longer about performance, ease of use, same solutions. I couldn’t even understand some post titles. There is just no bandwidth to follow these things anymore. Why is a router a thing that needs to be continually rebuilt and tinkered with. Did we not learn ages ago how routers should work? What innovation are we seeking? Is it just developers treating frameworks like their weekend experiments?
- sanskarix 11mo agoYou've captured something important here. There's been a shift from "solve problems" to "create novel patterns." The incentives are all wrong—framework authors get validation from innovation theater, not from boring reliability. I think part of it is that the web developer community exploded. More developers = more people trying to make their mark = more churn. Everyone wants to be the person who "fixed React" or "reimagined routing." But when you're actually building a product that users depend on, you realize how much of a tax this is. Every framework "upgrade" that breaks things is time NOT spent on features, user feedback, or actual problems. The irony is that the best products are often built with "boring" tech that just works. Instagram ran on Django for years at massive scale. Basecamp is still Rails. These teams focused on users, not on having the hottest stack. What frameworks/tools have you found that stayed stable and just worked over the years?
- twicetwice 11mo agowhat's the moderation policy/etiquette for calling out obviously LLM-generated comments? doing so feels like more heat than light, but letting them pass by without saying anything feels like another step towards a dead internet.
- svachalek 11mo ago
- sanskarix 11mo agoThe issue is everyone's optimizing for blog post metrics, not actual problems. "Look at this new pattern!" gets clicks. "We kept it simple and it just works" doesn't. Same thing happened with microservices - everyone rushed in because it sounded cool, then spent years dealing with distributed systems hell.
- Zaheer 11mo agoThe biggest facepalm moment I had was when we switched Levels.fyi from gulp.js to next.js. Our pagespeed, hosting costs, etc all took a significant hit. We're experiencing the same issues as described in the post and weighing our options to transition as well. Avoid next.js / vercel at all costs.
- culi 11mo agoWhat did you end up doing? Are you still on nextjs? Big fan of levels.fyi btw. Thanks for your work
- Zaheer 11mo agoAppreciate it! We're still on nextjs. Will def put a blogpost together as we optimize / move away. Thankfully, AI makes large-scale mostly repetitive migrations like these much simpler.
- yieldcrv 11mo agoWhere would you prefer to deploy now? Anybody passing by please share too
- Zaheer 11mo agoWe deploy currently on AWS so that's not changing. It's just the framework that needs to be changed :)
- saurik 11mo agoIf it sucked why didn't you just abort?
- chuliomartinez 11mo agoTanstack lack of back button handling is infuriating. Throwing away users data is simply not acceptable. Is Wouter better in this regard?
- aiiizzz 11mo agoElaborate? Back button handling, in what way? I've not used tanstack thus far
- poncho_romero 11mo agoAlso curious. I’m currently using TanStack Router and I haven’t noticed issues—and this is coming from someone who will stop using sites that break basic browser functionality like the back button
- bni 11mo agoI use the Pages router. Doing it this way make sense to me in a way that SPAs never did.
- daotoad 11mo agoThe crazy thing is that the Pages router looked to me to essentially be a React flavored version of the venerable Perl HTML::Mason library. It offered embedded code that is used to generate static HTML server side. It had application routing based on the file directory structure of the repository. It had automatic code wrappers distributed across the directory structure. https://metacpan.org/pod/HTML::Mason https://metacpan.org/pod/HTML::Mason I used the heck out of HTML::Mason for years and it scaled ridiculously well to large apps. Over time, HTML::Mason was supplanted by Mason, but I never used it due to not working in Perl anymore. https://metacpan.org/pod/Mason https://metacpan.org/pod/Mason If you like the Pages router, it wouldn't hurt to dig through ancient scrolls of Mason users to look for techniques and lost knowledge. Caveat: I haven't written or maintained any big apps in Next, but I've played with it a bit and have supported the SDKs I work on in Next apps.
- gherkinnn 11mo agoVercel has been a string of puzzling decisions since the introduction of the app router. Next could've become JS' Rails, instead it is a pile of confusing mess. Turbopack, caching, middleware (now called "proxy"), their layout components are silly, implementation of RSC, pushing for unfinished alpha versions of everything. Next is a conceptual mess of initialisms (RSC, SSR, PPR, SSG, ISG). Hosting integrations are semi-proprietary and they reliably break basic JS APIs like fetch and redirects. And despite all of that, they don't ship the basics that every app needs, like i18n and auth. Next should no longer be chosen under any circumstances.
- phoronixrly 11mo agoI can't imagine how slinging ungodly obfuscated JS blobs^w chunks over the wire can ever be the next Rails...
- gherkinnn 11mo agoThis is beside the point. And I can't imagine how anyone would want to write templates in anything other than JSX and yet here we are. Rails and friends aren't about a single technical choice, but providing a complete and opinionated framework to build web apps that conceptually fit in to one person's head. Rails is that. Django is that. Laravel is that. Next is the opposite, where Next-specifica around caching won't fit in to a single head, let alone the app you're building.
- phoronixrly 11mo agoHow can anything that is completely un-debuggable (like the mentioned chunks) fit in any one person's head?
- dzonga 11mo agothere was a hey day where react was concerned with client side stuff only - yeah redux was a little complex but you needed to learn it once, react router didn't change every two days. we built incredible enterprise stuff around that stack react (with classes), react-router, redux. that's the last time I enjoyed react. everything else is now a hamster wheel. running but staying in one place. marketing and money now drives everything else. Maybe there's a fire & motion strategy going on some of the places. things like inertia help, but not by much.
- zgoscenka 11mo agoFor a project at work we chose next because the team knew react and other people in the company know react. It is quickly becoming the worst architectural decision in my whole carrier. I wish we would've picked anything else...
- mosselman 11mo agoThe horror of needing to replace a routing layer. Why is this not a solved problem? This is an undervalued advantage of using steady frameworks like Rails that in essence is the same as 20 years ago, but with lots of extras. I don’t remember any big changes in the routes at least. Nor in any of the other basic building blocks. You could come back to rails after a 10 year break and pick up pretty quickly where you left off
- GamerYou54 11mo ago[flagged]
- tock 11mo agoI've used the app router and it is fairly nice once you understand how all of it works. But its a bucket load of complexity nobody really needs. A normal react SPA using vite + wouter + react-query works brilliantly.
- novoreorx 11mo agoI noticed the author mentioned the stack they are currently using is NextJS + Hono, and according to the code in the blog, Hono is used as the API backend to provide data for NextJS through calls like `fetchUserInfo`. This is where I really got confusing, why is it designed like this? NextJS is already a full-stack framework, not to say you used RSC, you can directly get user data from the database in NextJS. If you decide to use Hono for HTTP API, which is fine because it's more light weight and adaptive, then why RSC rather than making the frontend a SPA? Even if you use NextJS to write SPA it would looks much reasonable to have both NextJS and Hono used together.
- sktrdie 11mo agoI'll try to review the article with comments to make this a more critical discussion instead of just hates on Next.js (I'm just a Next.js developers for years now and am quite happy with it - but I do agree it requires some deeper understanding) > React is now using the words "server" and "client" to refer to a very specific things, ignoring their existing definitions. This would be fine, except Client components can run on the backend too It was hard discussions to come up for naming of these things in the beginning. Even calling them "backend" and "frontend" (as they suggest in article) wasn't clear about their behavior semantics. I understand the naming annoyances but it's a complex issue that requires lots more thought than just "ah we should've called it like this" > …This results in awkwardly small server components that only do data fetching and then have a client component that contains a mostly-static version of the page. > // HydrationBoundary is a client component that passes JSON > // data from the React server to the client component. return <HydrationBoundary state={dehydrate(queryClient)}> <ClientPage /> </HydrationBoundary>; It seems they're combining Next's native hydration mechanism with TenStacks (another framework) in order to more easily fetch in the browser? To follow on their WebSocket example where they need to update data of a user card state when a Websocket connection sends data. I don't see what would be the issue here to just use a WebSocket library inside a client component. I imagine it's something you'd have to do to in any other framework, so I don't understand what problem Next.js caused here. What they're doing screams like a hack and probably the source their issues in this section. > Being logged in affects the homepage, which is infuriating because the client literally has everything needed to display the page instantly I'm not sure I understand this part. They mention their app is not static but instead is fully dynamic. Then, how would they avoid NOT showing a loading state in between pages? > One form of loading state that cannot be represented with the App Router is having a page such as a page like a git project's issue page, and clicking on a user name to navigate to their profile page. With loading.tsx, the entire page is a skeleton, but when modeling these queries with TanStack Query it is possible to show the username and avatar instantly while the user's bio and repositories are fetched in. Server components don't support this form of navigation because the data is only available in rendered components, so it must be re-fetched. You can use third-party libs to achieve this idea of reusing information from page to another. Example of this is motion's AnimatePresence which allows smooth transitions between 2 react states. Another possibility (of reusing data from an earlier page) is to integrate directly into Next.js new view transitions api: https://view-transition-example.vercel.app/blog https://view-transition-example.vercel.app/blog <- notice how clicking on a post shows the title immediately > At work, we just make our loading.tsx files contain the useQuery calls and show a skeleton. This is because when Next.js loads the actual Server Component, no matter what, the entire page re-mounts. No VDOM diffing here, meaning all hooks (useState) will reset slightly after the request completes. I tried to reproduce a simple case where I was begging Next.js to just update the existing DOM and preserve state, but it just doesn't. Thankfully, the time the blank RSC call takes is short enough. This seems like an artefact of the first issue: trying to combing two different hydration systems that are not really meant to work together? > Fetching layouts in isolation is a cute idea, but it ends up being silly because it also means that any data fetching has to be re-done per layout. You can't share a QueryClient; instead, you must rely on their monkey-patched fetch to cache the same GET request like they promise. Perhaps the author is missing how React cache works (https://react.dev/reference/react/cache https://react.dev/reference/react/cache) and how it can be used within next.js to cache fetches _PER TREE RENDER_ to avoid entirely this problem > This solution doubles the size of the initial HTML payload. Except it's worse, because the RSC payload includes JSON quoted in JS string literals, which format is much less efficient than HTML. While it seems to compress fine with brotli and render fast in the browser, this is wasteful. With the hydration pattern, at least the data locally could be re-used for interactivity and other pages. Yes sending data twice is an architecture hurdle required for hydration to work. The idea of reusing that data in other pages was discussed before via things like AnimatePresence. What's important to note here is that the RSC payload exists at the bottom of the HTML. Since HTML is streamed by default this won't impact Time-to-first-Render. Again, other frameworks need to do this as well (in other ways but still, it needs to happen) I totally understand the author's frustrations. Next.js isn't perfect, and I also have lots of issues with it. Namely I dislike their intercept/parallel mechanism and setting up ISR/PPR is a nightmare. I just felt like the need to address some of their comments so maybe it can help them? As a first I would get rid of tanstack since it's fighting against Next.js architecture. Or yeah just move entirely elsewhere :)
- aiiizzz 11mo agoTanstack, last I checked, doesn't even support RSC.
- dbbk 11mo agoSo?
- kobalsky 11mo agowhy would you go through the trouble of doing SSR on a user profile form? it's not needed for SEO, caching it is pointless, the server won't render it faster than the client so you are not speeding up anything. what's the point of complicating anything about it? seems that at least some of their suffering is a self-inflicted wound, maybe the author just picked a bad example.
- adverbly 11mo agoIt's crazy that NextJS became the default new developer tooling. It's a bit unfortunate that whoever can scream the loudest with their marketing to get people to git clone their framework tends to grow the fastest. There are so many layers of abstraction that are simply not necessary in most frameworks and that risk over complicating or under complicating many products. It's almost like blindly defaulting towards any opinionated solution is not what we should be teaching people to do... /Sarcasm
- para_parolu 11mo agoI had to fully deattach routing and lazy loading because it was very slow and uncomfortable. Nextjs is well suited for half-static website that you need tomorrow. But once you get to something complex it starts showing so many issues.
- huksley 11mo agoApp router is an overengineered mess. "These rules are too hard for normal developers to understand." Just use pages router, it is still supported and works, also static generation for SEO also works.