5 issues I encountered while converting a create-react-app to SSR (and how I solved them)
Tags: react, javascript, tutorial
Published: 2019-10-19 05:27:08


*This is the first post in a series I'll be writing about the creation of my new webapp Readlist. Readlist is an app that lets you earn money by creating and sharing book lists. Check it out here --> https://readlist.io It's free!*

If you're new to ReactJS like me, you probably created your first app using `create-react-app` (CRA). CRA is a super easy, beginner-friendly way to get started with React, but it comes with a big caveat: your app will most likely be "client-side only", which means that search engines such as Google and social sites such as Facebook and Twitter probably won't read your metatags, and therefore can't index or create social cards for your pages.

If your web-app exists behind an auth barrier and you don't intend for the uninitiated to see it, then a CRA is probably fine. But what happens if you have a lot of outward-facing, publicly-accessible pages that desperately need to be SEO-friendly and read by the social networks?

####Possible Solution: Use `react-helmet`.
React-helmet is a library that supposedly lets you append metatags to your head tag, and therefore be more easily indexed. I tried react-helmet in my app, but after several days of debugging, it just wasn't working. Therefore, I decided to take a deep dive and start exploring something called server-side rendering (SSR) that I'd seen people talking about.

####Real Solution: Use Server-Side Rendering (NextJS)

Server-side rendering, like it sounds, is a way to render your page on the server before serving it to the end-user. Faster loading times is one of the main benefits, but for me, the real benefit is loading in data before the render, which allows me to customize HTML metatags on the page before it gets indexed. 

After some research, I discovered two frameworks for SSR: Gatsby and NextJS. Gatsby is optimized for delivering static pages, but Readlist has lots of content that will update frequently, so I opted to use NextJS.

With companies like TikTok, Nike, and Twitch using NextJS, you can be sure that NextJS is a capable framework for your SSR needs.

In the remainder of this post, I'd like to go through 5 difficulties I encountered while converting my CRA app to NextJS, and how I solved them.

####1. Handling Global State
Global state is a must-have for a single-page app (SPA) like Readlist, which handles authentication on initial page load.

In a normal CRA app, it's easy to wrap your main App component in a state provider like Redux or the React Context Provider. It's simple to do in NextJS as well, but it took me several times reading through the excellent NextJS docs to discover the proper way.

The trick is to create an `_app.js` folder in the `pages/` directory and extend the App class. Then you can wrap the Component class (a NextJS class) with your State provider. Not bad!

####2. Routing
Routing your pages in NextJS is a bit more complicated than in a CRA app. To convert your CRA app into a NextJS app, you need to create a `pages/` folder in the root directory and place all of your app pages there. 

Since NextJS allows for both server-side routing and client-side routing, it also uses a special Router component found in next/router. 

The NextJS router has its own custom Link component that handles client-side routing, but the syntax is a bit tricky. Link takes two properties: `href` and `as`. If you have a simple link like "about", then you can just use `href`. However, if you need a more complicated link like /user/coderdannn, you will need both `href` and `as`. In this case, `href` takes a *template* of your URL, with the dynamic part encased in []. So in the above case, it would be `href="/user/[userID]"`. Then in `as`, you actually write the real path: `/user/${userID}`. It took me several hours of trial and error to finally get that working!

####3. getInitialProps

getInitialProps is part of the magic of NextJS -- where the real server-side rendering really happens. NextJS pages are statically optimized by default, and therefore not server-side rendered. However, on those pages you want to contain metatags and be indexed or shared (such as user profile pages, or book lists) you will need to render them server-side. 

getInitialProps is the NextJS solution to that. getInitialProps can be called on the default export component of your .js or .tsx file, and it returns an object that, when finished, gets sent to the props of your default export. The page will not finish loading until that fetch has completed. For example, if I want to fetch a user object to display in my User component, I can call getInitialProps, await fetch my object, and then `return {userObject:fetchedObject}`. It's that easy!

Note that if you're using Firebase like I am, if you use getInitialProps to fetch your data, you cannot use the onSnapshot method to listen to changes in your objects. Which is kind of a bummer. 

####4. Environment Variables

Environment variables work basically the same way as in a CRA app, except you need to also declare them in the next.config.js file within an `env:{}` object. Then you can call `process.env.VAR_NAME` as you would in a CRA app. Big thanks to  NextJS lead developer @timneutkens for pointing this out to me. I have to say that the NextJS community is super friendly and always willing to help out!

####5. CSS and Dynamic Loading

NextJS by default enables code-splitting, which splits your bundles into multiple pieces. This is great for server-side rendering because it means your user has less data to consume before becoming interactive. In addition, you can also dynamically require components using next/dynamic, which will split that component into its own separate bundle. 

One area to be careful is in your external css modules. For Readlist, I used a lot of global external CSS modules, which turned out to be pretty inefficient in NextJS because the entire CSS module had to load before it can render the page. NextJS recommends you use local CSS whenever possible, and it uses a special `styled-jsx` library to achieve that. One of my biggest speed optimization gains was copying all of that external CSS into the files that consumed it. 

####Further Thoughts

Converting my CRA app into a NextJS SSR was definitely a painful task, but then again, so is learning anything new. Thanks to all the great community members for writing great docs, and also for creating dozens of examples of NextJS on Github. 

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/t525au5404ojvwg1xt0l.jpg)

My app performance still isn't perfect yet, as you can see above, but by converting to SSR, I achieved my main goal: allowing users to share their lists to Twitter and Facebook. High five! 

I'm active on Twitter so if you'd like to chat about developing, startups, books, living in Asia, or anything else, feel free to DM me! @coderdannn

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/w83iotgqlaxz0m01oe8p.jpg)