6 tips: Using Next.js for your next web app

Tags: nextjs, react, javascript, frontend
Published: 2019-08-29 05:40:02

Next.js is indispensable when you want to build a high performance React app. 

It’s being used to power some of my apps such as [Medtally](https://medtally.com), a data-driven medical community and [True Home](https://truehome.hk), an automated home valuation tool for properties in Hong Kong.


Along the way, I learned a few tricks and figured out a few “gotchas” of the framework that beginners might find useful. And without further ado, let’s get started:

### 1. You need to cache the data from getInitialProps or the browser’s back button behavior will break.

`getInitialProps` is used to fetch data for the page but it also fires when the user presses the back button on the browser. This causes the browser to scroll to the position where you previously left off but without the remote data from `getInitialProps` needed to render. You can read more about this issue [here](https://github.com/zeit/next.js/issues/3303).

To solve this problem, you need to cache the data on the client side after fetching it. Here’s a quick and simple way to do it:

```javascript

let cache = {};

class App extends React.Component {
  static async getInitialProps({ req, query, asPath, pathname }) {

    let data;
    //if data is in cache then use the cache
    if (cache[someID]) {
      data = cache[someID]
    } else {
      //if not, then fetch from server
      data = await fetch(`someAPI`);
    }
  
    return {
     data: data
    }

  render() {
      //check if client, if so store the data in the cache. 
      //If you don't do this check, there will be a separate cache stored on the server since Next.js does server side rendering as well.
      if (process.browser) {
        cache[someID] = this.props.data;
      }
    
      render (){

       //your components
      }
    }
  }
```

You can also do this [with Redux](https://stackoverflow.com/questions/42980613/how-do-i-implement-caching-in-redux).

### 2. Use Next.js’ Link component instead of Router.push so Google can crawl your URLs

Google’s crawler doesn’t see links written like this: `<div onClick={handleClickWithRouter}>Go to About Page!</div>`

So avoid writing your links with Next’s Router if possible. Instead, use Next’s `<Link>` component like this:

```javascript
import Link from 'next/link'

function Home() {
  return (
    <div>
      Click{' '}
      <Link href="/about">
        <a>here</a>
      </Link>{' '}
      to read more
    </div>
  )
}

export default Home
```

### 3. Next.js works better with Material UI React than Semantic UI React

If you’re trying to choose between Material UI and Semantic UI React component libraries to go along with your Next.js app, you should choose Material UI.

Semantic UI’s responsive components aren’t built in a way that works well with Next.js’ server-side rendering because they look for the browser’s `window` object which isn’t available on the server.

If you must use Semantic UI, you can hack it together by following this GitHub [ticket](https://github.com/Semantic-Org/Semantic-UI-React/issues/3361).

On the other hand, Material UI’s responsive components only use media queries which means your components should render in the same way on the server and in the client.

If I convinced you, head over to Material UI’s [Next.js example](https://github.com/mui-org/material-ui/tree/next/examples/nextjs) to get started.

### 4. If you use isomorphic-unfetch to do your data fetching, you’ll need to provide the absolute URL

The Next.js creators recommend a library called `isomorphic-unfetch` for data fetching. It has a much smaller bundle size than `axios` and works well on both the client and the server.

However, isomorphic-unfetch requires an absolute URL or it will fail. I’m assuming it has something to do with the different environments (client & server) on which your code can be executed. Relative URLs are just not explicit & reliable enough in this case.

You can construct an absolute URL from getInitialProps like this:

```javascript
class App extends React.Component {
  static async getInitialProps({ req, query, asPath, pathname }) {
    
    let baseUrl = req ? `${req.protocol}://${req.get("Host")}` : "";
    
    let data = await fetch(baseUrl + 'relativeURL')
   
    
    return {
      data: data
      
    }
    
  }
```

5. Store your URLs in the database if you want the prettiest of pretty URLs

URLs should be pretty so people want to click on them when they see them on Google.

You generally want to avoid having the database table id in your URL like this: `/post/245/i-love-nextjs/`.

Ideally, your URLs should be something like this: `/post/i-love-nextjs`. 

But this URL is missing the id 245 needed to fetch the data from the database.

To solve this problem, you’ll need to store the URL in the database like this:

| id        | title           | Content  | url |
| ------------- |:-------------:| -----:| -----:|
| 245	     | I love Next.js | because... | /post/i-love-nextjs |

On the server, write code to fetch data using the URL in lieu of the post id when someone requests mywebsite.com/post/i-love-nextjs. Here’s an example using Express.js:

```javascript
server.get('/post/:slug', async (req, res) => {
    const actualPage = "/post";
    
    //get the data using '/post/i-love-nextjs' as the id
    let data = await fetchDataWithURL(req.params.slug)
    
    const postContent = { data: data  };
    app.render(req, res, actualPage, postContent);
  });
```


I recommend `slugify` if you need a library to convert text into URLs.
If you have many URLs and you’re using a relational database, you should consider adding an index to the url column so that your lookup query runs faster.

6. CSS breaks in prod but not development when using Material UI with Next.js

```javscript
function createPageContext() {
  return {
    theme,
    sheetsManager: new Map(),
    sheetsRegistry: new SheetsRegistry(),

    
    //add this to fix broken css in prod
    generateClassName: createGenerateClassName({
      productionPrefix: "prod"
    })
    
    
  };
}
```

If this happens to you, try adding this line to your getPageContext.js file:

And that’s it!