Everything about our migration from ReactJS to NextJS
The overall thought process, challenges, and outcomes: Peerlist's tech stack migration from ReactJS to NextJS.
Hello folks đź‘‹,
Two months back, in our November Release, we announced our tech migration. Since then I was planning to write this blog but thought to take some time to have better clarity on everything. As things are quite stable now (but we are still improving), I thought of sharing the whole thought process and how we took our decisions.
Spoiler Alert! This article covers most of our thoughts behind the decision and the process, and might not go deep in tech, but follow along, in the end, it will be worth a read!
React is one of the most popular javascript libraries and is widely used in so many applications these days and NextJS is a framework built on ReactJS. You don’t understand the true power of Next until you start using it and I am saying this from my own experience!
I have been working with React for more than 3 years now and I always loved the way it works. So when we started building Peerlist from scratch, React was my very obvious choice. Because of the MVP stage, we thought of working with our strengths (which was of course React for frontend) and building the first working prototype in ReactJS.
Initially, this worked, we were able to ship within our timelines and everything was working smooth. Though soon we realized the decision of going with plain React didn’t turn out to be so good for us. We knew that this tech stack won't scale with the product roadmap we have in place.
Why?
All technologies and frameworks are amazing but they are created to fulfill different use-cases. So when I said plain React wasn’t good for us, I was thinking of the following use-cases,
We needed a more SEO-friendly framework.
React is pretty good at creating Single Page Applications but Google crawlers find it difficult to index and fully process the Javascript of your app. This will start affecting your SEO. For the websites like Peerlist, the user’s content is the Hero.
We wanted your Peerlist profile should be in the top 5 search results when someone is looking for you or for a professional with similar skills as you. We had to crack the Google search algorithm to display your Peerlist profile.
We all know that SEO takes a good amount of time to build and we lost our initial couple of months by not getting indexed and ranked enough by Google. This became a deal-breaker!
Server-side rendering support.
We had two very particular use-cases which needed our app to support server-side rendering (SSR). One of them was SEO which I already mentioned above and the second was the custom social previews. Something like this -
Peerlist's social preview when you share your profile.
For the sites like Peerlist where our focus is totally on highlighting users’ data, we needed the social preview of every user profile’s link customized for that user. If I want to share my Peerlist profile link, my info should get highlighted than the platform. You must have seen such custom social previews for sites like GitHub, DEV, and Hashnode.
Both of these features needed SSR and we did not find a good, robust, and scalable solution that can fit our requirement of turning a react app into SSR.
Routing
React apps heavily rely on React-Routers. Though React Router is an amazing library to handle routing, we started facing difficulty to maintain and follow conditional routing using it. Though React Router would have worked with a more refined implementation, we started looking for something easier to maintain, implement and scale.
Javascript Ecosystem
In our earlier implementation, our backend was developed using Springboot and Postgresql. This was a wonderful combination and we hardly face any difficulty with this. Though this was the case, we decided to move completely into the javascript ecosystem. It was more for the ease of development and leveraging the advantages of a single project structure and MongoDB.
But then, what next? NEXT.
With all these use-cases in our minds, we figured Nextjs was our ideal fit. Next is an opinionated framework that provides out-of-the-box support for SEO, SSR, routing, and API routes to create serverless APIs. We wanted everything and the addition of performance benefits.
Particularly these are the benefits of Next which we considered while choosing it
- SEO and image optimizations.
- Optimized bundling and code splitting, to improve the app performance.
- Very intuitive and dynamic page routing.
- API routes to support serverless APIs.
- Built-in server-side-rendering support.
- A framework built with React
Migration process and challenges we faced
We started understanding the downsides of our earlier implementation, but the question was when is the good time to migrate?
To set you a little context, we had launched a closed beta of our app two months back and were in the process of shipping new features, testing them, and collecting more and more user feedback. So, we had to decide between shipping new features vs migration?
Because of a very small engineering team (🧑‍💻 x2) doing both in parallel was not the option. But taking up the migration means we have to pause feature development. Still, we decided to go ahead with migration because the earlier, the better!
Considering the earlier Reactjs project, frontend migration was a little easier. Most of the previous components were reusable. The only difference we consider was in the following four things
- Moving from React Router to native Next router
- Adding SSR for certain pages
- Changing the folder structure as per Next
- Creating custom head components for meta tags to improve SEO
From this, frontend migration seemed pretty straightforward. What needed to do was, write the backend from scratch. As I mentioned, our earlier backend was in Springboot and Postgresql, moving it to javascript APIs with MongoDB meant writing and structuring everything from scratch.
For this migration, considering our timelines and resources, we decided to replicate everything as is without modifying it. Because we wanted to do it as quickly as possible and keep improving it later. But again, who has control over that developer’s urge to refactor the code and implementation?
But on a positive note, this migration gave us the chance of improving the implementation approaches. We made our system more refined and stable. Though these improvements made us miss the migration deadline, the overall improvements we experienced in our system were worth those efforts.
If I need to summarize the whole migration process and write down the learnings, here are those -
- Initially, I used to feel that we should have given more thought to selecting the correct tech stack in the first attempt. But always remember, your first attempt will never be a polished and perfect product (that’s why it is called prototype!). You are already testing your idea, so it's okay if you play with your strength and select the tech stack you are most comfortable with.
- No system can be ever made perfect! We all have seen bugs in the well-known apps, crashes happening with applications that we consider ideal, so you creating something with your best effort is all that is needed. Bugs will be the part of your software as features are, the point is always in improving your system and minimizing them, not making a perfect system.
- Code refactoring and improvisations are good, but time-boxing them is essential. Otherwise, we fall into a rabbit hole.
That’s all I wanted to share from our migration process. I have deliberately tried to keep this article less technical and more of a thought process we went through to make it a little relevant. Do let me know in the comments or reach out to me on Twitter if you want to understand any particular part of the process. I will surely try to cover it in my next article.
Till then, keep exploring! ✌️