Adopting the App Router
In Tianguiza, we have been using the Next.js App Router for 8 months, and it has been a great experience. In October 2023, we started developing Tianguiza, an app for street markets in Mexico. With a team of three developers, we had to decide on a tech stack. I chose Next.js and the app router because I liked where it was headed. In retrospect, now that the app is released, I am happy with that decision.
From the start of the project, we decided to adopt the new app router model, especially server actions and server components. Since I was the only one on the team with some (albeit very little) experience with the new model, it was my responsibility to make it as easy as possible for my team.
The New Model Change
Since we (and almost everyone at the time) only had experience with React before server actions and server components, the most difficult part was the server/client boundary separation. We frequently encountered these types of problems:
- How do I make mutations from the client?
- Why can't I import a server component into a client component?
- Should we stop using all these new features and start fetching everything on the client?
These issues caused a slow start as we adapted to the new paradigm, but after some months of development, they stopped being a problem as we learned how to use it.
Some Recommendations
Server Actions
- Be careful with security: As server actions do a lot for us, it's easy to forget that we are calling an "API," so security can be fragile if not used correctly. I highly recommend reading How to Think About Security in Next.js from the Next.js team. It helped me understand how to approach security when using server actions.
- Always revalidate: Always revalidate all information on the backend. We used Zod for this and always revalidated sessions. Don't trust anything outside a server action.
- Use server actions outside forms: You don't need to use server actions only inside a form. We handled server actions with JS for more flexibility with validation and pending states. Also, we liked the DX more.
Server Components
- Fetch on the server as a default: Fetching on the server has many benefits; it saved us from setting up a state manager solution and it simplified our code a lot.
- Leverage Suspense boundaries: We didn't use these as much as I would have liked, but if you're using server components as your primary fetching solution, Suspense boundaries are easy to use and can make the app feel much faster.
- Use the URL to keep the state: Server components pair well with having the state in the URL, so why not use it?
Caching
- Use the default Next.js cache configuration: This might be an unpopular opinion, but for our use case, the default Next.js caching behavior was unproblematic. We handled revalidation with revalidatePath, and so far, it has worked without causing issues. Just make sure you revalidate all necessary routes.
Conclusion
Our journey with the @nextjs app router over the past 8 months has been both challenging and rewarding. Developing Tianguiza required us to navigate new paradigms in web development, especially with server actions and server components. Despite the initial learning curve and the difficulties our persistence paid off. You can check out the App at tianguiza.com or download it on the Play Store.
Some References
- The Two Reacts A post where Dan Abramov gives a great introduction to server components.
- How to Think About Security in Next.js A post by Sebastian Markbåge on How to secure our app in App Router.