Christian Vuerings

From Fast to Lightning Fast: Cambly's Next.js App Router Conversion Journey

At Cambly we converted our kids landing page to the Next.js app router which boosted our Lighthouse performance score from 91 to 100! Our experience led to gaining a few key insights that we believe are worth sharing.

Results

Our Lighthouse performance score has improved from 91 to 100. Refer to the following table for a breakdown of the specific metric changes.

MetricPages DirectoryApp RouterImprovement
First Contentful Paint1.9s0.9s53%
Largest Contentful Paint3.2s1.2s63%
Speed Index2.4s1.3s46%

Lessons Learned

1. Use React Server Components when possible

React Server Components are a new React feature that allows us to render components on the server. This has a few key benefits:

We used React Server Components for anything that doesn't require client-side interactivity, React context or React state and side-effects.

2. Refactor from the top down

We started by converting to top level entry page & layout to React server components and then worked our way down. That allowed us to get the big performance wins quickly and spread out the conversion over time.

3. Co-locate data fetching with components

With the Next.js App Router we can now co-locate data fetching with components. This means that we can fetch data in the component that needs it. It makes it easier to reason about the data flow and makes it easier to refactor.

Next.js automatically caches fetch() calls during a request. So fetching the current user in multiple components will only result in one network call. If you want to manually cache data, you can use the React cache method.

Previously if we wanted content to be rendered on the server, we had to put all of our data fetching in getServerSideProps. That meant we had to do lots of prop drilling or use React context to get the data to the components that needed it.

Gotchas

1. Internationalization is no longer built-in

Previously we used the i18n property in next.config.js to enable internationalization in combination with next-i18next. When we use the Next.js App Router, that is no longer required and we can use react-i18next directly.

Huge thanks to Adriano Raiano who provided JavaScript and TypeScript example with the Next.js app directory:

2. #__next is gone

With the Next.js App Router, your page content no longers renders insides of a #__next div. Be sure to update any code that relies on that.

3. TypeScript doesn't yet support React Server Components

The latest version of TypeScript (5.0) doesn't yet support React Server Components. It should be fixed in TypeScript 5.1 but for now we had to use the following workaround:

// Async server components workaround
export default ExampleComponent as unknown as (props: Props) => JSX.Element;

Conclusion

It's still early days for the Next.js App Router but we're excited about the performance improvements and the ability to co-locate data fetching with components. The Next.js team did a great job of making the transition as smooth as possible.


Personal blog by Christian Vuerings
I love to share interesting ideas.