It has been a few years since I have been working on developing client-side applications using React.js. These applications mostly required instant updates and fast response times using REST APIs which we are able to achieve using Client-Side Rendering (CSR), which simply means sending over the JS files to the browser and the browser will run the JS to render the HTML. The main benefit of using client-side rendering is that you do not need a server to run your client-side application. The major drawbacks of using CSR are slow initial load times and difficulty in SEO.
The other option is Server-Side Rendering (SSR). SSR is the predecessor of CSR. In SSR applications, a server compiles a complete HTML file and sends it over to the browser and the browser just has to render that HTML. This does overcome the drawbacks of CSR but SSR comes with its own drawbacks. In SSR, the browser has to call the server to get a new HTML every time it needs to get updated data. This approach is a killer for data-driven applications. This problem and the fact that client-side JS engines were getting more efficient are the reasons that client-side development moved from SSR to CSR.
But the drawbacks of CSR are still crucial for a lot of organisations. The only way to overcome all the drawbacks is to use a hybrid approach that can provide SSR with the abilities of CSR. This is where the Next.js framework comes in.
The Next.js framework provides us with a hybrid approach in which any request to your server is handled by Next.js with an SSR response. In addition to the HTML of the page, this SSR response also includes CSR bundles that will help the user to navigate the rest of the application without calling the server again. This ensures that the initial load time is low & as the SSR response has a complete HTML, SEO is also very effective. Additionally, Next.js also provides us with a lot of tools to tune the hybrid approach according to our needs, but we’ll come to that later.
Diving into your Project:
The Vercel team has done an outstanding job making the project setup extremely simple. All you need to do is run the following command on the CLI to set up a new Next.js project.
I would recommend using the
--typescript flag to set up the project with TypeScript (TS) already installed. Properly creating and using TS types in your application makes development much easier and you’ll be able to avoid so many production bugs.
Apart from the basic setup, you might want to install SASS for styling, Prettier for code formatting, and SWR which is a great tool for fetching and caching data. If you do use SWR, you might not even need global state management like Redux.
By default, you’ll have
styles directories. The
public directory can be used to just host static files like
robots.txt. You should create a
src directory and move the
styles directories inside that. Next.js uses the
src/pages path to build the project. Each JS or TS file inside pages is considered to be a page on the application.
Now, in my opinion, the best
src file structure you can have is:
api - API related functions & variables
components - Commonly reusable components
config - Commonly used configurations & constants
contexts - Commonly Reusable contexts
hocs - Commonly reusable HOCs
hooks - Commonly reusable hooks
pages - Pages & global files like _app or _document
styles - Global styling & variables
types - All the custom type definitions
utils - Commonly used utility functions
views - A view for each page, components inside subdirectories
PS: This is a very opinionated file system, it can be modified as seen fit.
You should visit the Next.js docs to find more details on Routing.
Note: You should maintain and use a routes file with functions that return the path of each page that you have created.
Post this point, you can feel free to start your development process.
There is a huge scope for optimising your application when you use Next.js and all of them are provided by the Next.js framework itself.
Next.js provides a
next/image library which provides us with a custom Image component that can be used to display images in an optimised way. It adds lazy-loading to the images by default, it provides a way to load size optimised images with Media Delivery Services like Imgix or Cloudinary, blurred image placeholder, image file caching, etc.
Next.js provides a
next/script library which provides us with a custom Script component that can be used to load external scripts in an easy and configurable way. You can set a strategy to tell Next.js how & when the script shall be loaded. It is pretty useful when integrating analytics tools.
There are various build-in data fetching methods that can be used to optimise your pages.
- getServerSideProps: This method can be used on a page to fetch the data required by that page on the server-side. This is useful on dynamic pages for SEO & faster initial load times. It will only be called on the server-side on each request to the page.
- getStaticProps: This method can be used on static pages that may use data from an API. It is called at the time of the build, it generates the static HTML file for the page and serves that file on each request without any processing. It can also be used for Incremental Static Regeneration (ISR) in which you can set a
revalidatetime limit for the page to be regenerated after a certain amount of time.
- getStaticPaths: This method can be used if you want to use getStaticProps on dynamic pages. It will also be called on build time, you need to return a list of queries that can be used by getStaticProps to generate static versions of the dynamic page for each of the queries.
SSR & CSR have their own pros and cons but Next.js really bridges the gap between these 2 technologies. This does not mean Next.js is the only way to go when developing an application, you’ll have to wager your requirements in order to get the best possible solution.
“Wherever smart people work, doors are unlocked” — Steve Wozniak
Thank you for reading this!