Hacker News new | past | comments | ask | show | jobs | submit login
We improved React loading times with Next.js (causal.app)
68 points by Lukas1994 on Oct 24, 2022 | hide | past | favorite | 50 comments



I've been checked-out of the SSG world for a couple years and am now evaluating different tools and frameworks for a project at work.

I'm intrigued by the direction being taken by Remix, which posits that today's backend options are so improved compared to those of the recent past that they (arguably) have eroded the utility of a static site, period.

https://remix.run/blog/remix-vs-next

When it's not crazy difficult or expensive to have a very fast app _and_ database close to your users, you don't have to architect around that deficiency, which is what SSGs are designed to do.


Though it's worth keeping in mind that Next's next big structural update [1] is pretty much an attempt to assimilate some of the basic concepts of Remix wholesale.

[1]: https://nextjs.org/blog/layouts-rfc


Not exactly – this has been in discussion for many years. From the post:

> Some routing conventions were inspired by the Relay-based router at Meta, where some features of Server Components were originally developed, as well as client-side routers like React Router and Ember.js. The layout.js file convention was inspired by the work done in SvelteKit.

No doubt React Router has certainly impacted the entire React ecosystem, though!


Interesting. Ultimately neither one of these companies stand to make money selling a web framework, but one of them happens to have a business for which it makes sense to commoditize and control its complements.

Put another way: Remix may have underestimated the value Next represents to Vercel.


remix comes with the main caveat that if your API is slow or very far away you will end up with a slow frontend, whereas next.js prerenders pages meaning they're always going to be fast. obviously this doesn't matter that much if you're making a customer dashboard but if you want to have public facing data and your backend isn't globally replicated you are either going to have to implement your own caching layer or your users are going to have a bad time


What does next.js offer if server side rendering is of no relevance? It seems to me that ssr adds a lot of complexity and a great number of projects have no need for it at all.


It takes care of all the boring stuff like reliable reloads, .env files, routing, config (without me writing any), bundling, etc. It leaves me the fuck alone.

SSR doesn’t add too much complexity and at the very least is a reliable way to serve a server-rendered shell of the application so the content doesn’t fly about as it loads.


100% this. Spinning up a new Next project "just works" for getting started, and makes me confident in a scaling path as times goes on. Adding things like lightweight API routes (weirdly useful in a ton of situations) or pulling in interactivity are trivial.

I still write static HTML if I'm positive it'll be a forever fit - but anything client facing or with an ambiguous feature I reach for Next.


I also like how it adds a light API layer, you can proxy 3rd-party APIs to your front-end.


This is a huge advantage when you’re building client-side apps that need a light backend.


Curious to hear from people out there using Next.js, but without _any_ SSR, i.e. using the `next export` command mentioned in the article: https://nextjs.org/docs/advanced-features/static-html-export

(Seems like the folks in the article are beginning to use SSR, and have some great use cases with publicly available, SEO-friendly content, but a lot of apps don't, and can benefit from avoiding the operational complexity.)

Create React App is a bit long in the tooth these days, so I can imagine `next export` being a lot nicer to work with in a whole bunch of ways, but the only advantage of `next export` I can think of over something more modern like Vite is file-system routing.

For those using `next export` but have also evaluated Vite, is FS routing what made you choose `next export`? Or is it something else?

Disclaimer: Asking with ulterior motives. I'm building https://reflame.app, a Vercel competitor focused on client-rendered React apps, with millisecond deploy times to previews and production. We currently have decent compatibility with Vite and CRA, but considering if we should try to add support for apps using `next export` as well.


I’ve used the next export based approach on quite a few projects and agree with previous commenters, next takes care of a lot of the boring stuff when setting up a new project and the routing feels refreshingly out of the way compared to many pre-next alternatives.


Well true SSR allows you to send zero javascript to your client, so this is ultimately a net positive. My site is written entirely in React because I like working with it, but thanks to Next.js you as a client will never know the difference between that and me serving static HTML in the first place. All pages, subpages and blog posts ship in a relatively tiny 200KB bundle.


Unless you’re talking about SSG and not SSR, I think one potential issue would be that React’s SSR process is pretty slow and costly, at least from what I understand. I have to confess I haven’t heard of a pure-SSR React app before, as you wouldn’t get many of React’s benefits if it’s just being used as a templating language.


> as you wouldn’t get many of React’s benefits if it’s just being used as a templating language

You'd think that, but I like JSX as a templating language.


It's really no more costly than Django/PHP/Ruby mustache-style template rendering. I consistently see 100-150ms last byte timings from a Next.js app I host on Vercel.


The obvious argument, tried and cliché as it may sound, would be that SPA are anti-web. A healthy web must be indexable and searchable. It almost seems like the rulebook now for some newcomers is to start out with SSR, make use of the auspices of the open web that allow for easy and organic discovery, - and after a significant marketshare has been captured, erecting all types of of lock-in walls, including turning SPA.


SPAs can be indexed, so the entire premise of your post is invalid.


Maybe Google can by traversing the virtual DOM and other trickery but it's not as simple for other small players. Moreover, in a healthy web I should be able to easily deeplink to something I'm thinking about without jumping through hoops. The SPA web is not as easily discoverable and connected as it is very much meant to be. I think there are valid arguments to be made for SPA, like that SPA need to SPA because they are not just a series of pages and documents but complex applications which need to be that way (though I would still tread with caution: I remember java applets and javascript bits doing complex things, submerged within pages, and that was all fine).


You are conflating things. With urls you can link fine, spa or not. It’s possible to design a single page server side rendered page without properly implemented links.

The things you are describing are not attributable to spa inherently, just a poorly designed site


Has it gotten easier to do deep links correctly with SPAs?

Back when I explored this it felt like working against the grain.


With a lot of SSGs, you can pre-generate all your "entry points" and then after the initial entry, it can be a SPA experience where a client-side router takes over navigation.


You can deeplink with SPAs just fine.


Not sure you understand what indexing is.


That’s literally what the article is explaining. Better routing system, and bundling, and a simpler optimized config for output.


But that is a lot of complexity and dead functionality to drag into a project for just that? Surely next.js's router is not that different from React router.


In our experience, using Next.js reduced our complexity, because it has out-of-the-box implementations (and simple configuration) for lots of stuff we had to manually hack around with CRA as our project grew


nextjs is crap but IMO doing react ssr on most requests with hydration is faster, more robust and less complex than going full csr/spa.


I'm one of the engineers that worked on this - would love to hear any thoughts or feedback anyone has! How does your team deploy your Next.js frontend?


I'm curious where the majority of the win came from? Do you have a before & after trace handy to compare what changed?

Looks like you opted not to use SSR, which I think is a good choice for smaller teams. CSS changes are unlikely to account for a large performance win. Did you use a CDN before? Perhaps the biggest win was moving to a better CDN + built-in preloading/prefetching provided by NextJS?


> I'm curious where the majority of the win came from?

Great question! The boost can be explained by JS vs. raw HTML+CSS performance.

In the past, we sent a dummy loading screen but we then had to download tons of JS + run that to even show a "real" loading screen (i.e. page-specific loading screen).

Now, the HTML response (which you can inspect yourself, `curl https://my.causal.app`) already has a skeleton of the page, which doesn't need any JS to download and run before showing the "real" loading screen.

The UX is better because users spend noticeably less time staring at a blank screen and there's much less layout shift too!


Which metric improved by 70%? Time to show the skeleton? I agree the skeleton is quite good. I see it takes about 350ms to show the skeleton and about 1s to show my content.


Yep - the metric was First Contentful Paint, which is basically the time-to-skeleton in our case :)


> backend API calls will be very fast because of colocation on GCP

Any chance you guys measured this speed difference? Was there a path considered to locally cache api responses on the vercel server or in middleware somewhere?

Trying to reason about a similar decision right now. There's probably a simple way to cache hot backend request/responses to get static-like speeds?


> Any chance you guys measured this speed difference?

No, but we actually can get some comparison, since as I mentioned we host preview apps on Vercel! I did some quick tests right now, and it looks like based on logs, the API request to our Node backend takes:

- From Vercel: ~300ms worst case, ~40ms best case

- From within GCP: ~120ms worst case, ~20ms best case

These numbers are very approximate, but hopefully still insightful! And I'm not sure about the cause of the variance, there are quite a few links in the chain that could be at fault.

> Was there a path considered to locally cache api responses on the vercel server or in middleware somewhere?

We do cache the responses using a CDN; adding another layer of caching beyond this wouldn't help much since at that point we need to fetch fresh results anyway.

> There's probably a simple way to cache hot backend request/responses to get static-like speeds?

Yes, the CDN cache I mentioned is almost instant in good conditions (~2ms on my internet connection) - the problem is sometimes you do actually need updated data, and having huge variance between the cached response speed and the uncached response speed could be undesirable for users.


thanks for those figures! It makes sense on the cache invalidation given the type of content Casual is serving.

It will be interesting once Vercel offers colo'd data sources and more with their edge rendering, which they just announced: https://vercel.com/blog/regional-execution-for-ultra-low-lat...



I'm so excited about this . It's a testament to how well-built-up modern frontend tooling is, especially around React. The things that used to take hundreds or even thousands of lines of config can now be done in a few dozen at most with managed libraries. And better!


Unfortunately too much JS has made the web slower, check this https://www.speedcurve.com/blog/web-performance-page-bloat/ for a quick view or infrequently.org for longer explanations. React starts to be dated now and there are leaner alternatives offering most of the functional advantages, like Svelte or Lit.

The tooling evolves indeed and I hope it gets again nearer to the browsers, thanks to web components, better CSS (hello variables), WASM and much more.


Great stuff — thanks for the write-up! Do you mind sharing the Dockerfile you use for deploying?



If possible definitely use Vercel :)


Ssr to improve a static app performance? Sounds like madness to me. More complexity plus much larger surface to secure.


One nice thing about Next.js is you don't need to use SSR unless absolutely necessary - it will statically generate all your pages by default (which is similar to but not the same as SSR - it runs once at build time rather than on every single request), so the result is similar to CRA but with quicker first page load times and a much better developer experience.

But you have the power to use SSR where necessary too, which is not really possible with CRA (without unscalable hacks).


I mean good for them for improving their user‘s experience. I am just surprised that maintaining your own webpack config seems like such challenge. Enabling chunks to split code to improve loading times or dynamic imports, it‘s not exactly rocket science. Yes DX and all improved as well, but CRA is such a bad choice for production code. It gets you going fast(er), but as soon as you have to tweak it, it seems like devs jump to the "next" framework that already has those tweaks builtin…until they hit the next roadblock. Thus you never learn what actually makes it all work.


Imo it's a question of prioritization - time spent fiddling with a Webpack config is time that could have been spent on something that will more immediately benefit a user. That's the big advantage we saw for switching to Next.js, which handles this and many other details for us, without the confusing & brittle hacks that had accumulated in our CRA setup.

> Thus you never learn what actually makes it all work.

I agree this is unsatisfying - however it's a problem no matter what! We have to live at some level of abstraction in order to make any forward progress. There can still be lots of learning along the way!


The problems described in the "Routing" section are trivially automated. You do not even need a separate data loading library because react-router-dom ships one.

The problems described in the "Styles" section would have been solved by migrating to a modern bundler: vite.


> The problems described in the "Routing" section are trivially automated. You do not even need a separate data loading library because react-router-dom ships one.

I'm not sure which problems you mean; fwiw the routing section is more intended to explain how we migrated `react-router`-style routing to `next/router`-style routing :)

> The problems described in the "Styles" section would have been solved by migrating to a modern bundler: vite.

I think the problems with impure styles (e.g. `styles/button.scss` referenced in the post) would have been an issue no matter what bundler we used, right? Which problems are you referring to?


What cause the performance improvement?

Maybe I missed this detail. I couldn't find anything on why the performance improvement happened when they moved from CRA to Nextjs


I explained it a bit more here: https://news.ycombinator.com/item?id=33319806

Let me know if this still doesn't answer your question though!




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: