Grade Calculator: Next.js Static HTML App
Tags: nextjs, react, javascript, firebase
Published: 2020-03-12 07:33:06


I worked on [10converters.com](https://10converters.com/calculators/grade-calculator) as my React practice in past month. After some research I found next.js static HTML app is the best way for the project. Hope the sharing could help someone here.

# Why Static HTML

Static HTML is the best way for small websites, like blogs and online tools. The benefits of serverless includes:

* easy to deploy, static HTML + cloud functions(if necessary)
* cloud vendors manages scaling
* benefit global CDN of hosting vendor
* SEO friendly

But also I found some problems of it, I will mention that later.

# Why Next.js

I started this project from simply React + Material UI components. Soon I found search engines doesn't like this pure React because most search engine bots can't handle JavaScripts. 

Then I turned into static HTML, tried several solutions like react-snap, react-snapshot. None of them could render every page correctly, until I found Next.js.

Next.js is not limited to SSR(server-side-rendering), it also could 'export' the whole site into static HTML, and it works perfect for all my pages.

What you have to do is just write your app follows Next.js instruction, and then run:

```
next export
```

Then you get everything you want. I assume you already know something about Next.js. Now, let's dive into only the part of static HTML exporting stuff.


# getInitialProps

Next.js use getInitialProps() to generate dehydrate data. I will show you that it is not mandatory for static HTML apps.

in SSR mode, getInitialProps() will be called in case of:

* in server side, if the app is not loaded by browser yet(a new request to your app)
* in client side, if the app is already loaded, and navigating to a new page via next/Link component

in static HTML mode, getInitialProps() will be called in case of:

* while 'next export' is executing, that's also server side rendering, but not in runtime, but during transpiling.
* in client side, if the app is already loaded, and navigating to a new page via next/Link component


For SSR, getInitialProps() works perfect, but not in static HTML mode.

The reason is in static HTML mode, getInitialProps() is called during transpiling, and then dehydrate data are generated before deploy. 

If your page renders depends on URL parameters or depends on something related to realtime stuff, like timestamp, user properties, getInitialProps() won't help.


It could be executed in client side, but only if the app is already loaded and navigating to new page via next/Link component.

# Links

Never forget that we have static HTML apps for better search engine bot experience, not for better user experience.

Links are essential feature of web, only \<a\> tag could be recognized by bots. 

For example, if you want to put a button which will navigate to another page, do like this:

```
<Link href="/to-another-page" passHref>
    <Button component="a">Another Page</Button>
</Link>
```

make sure replace default \<div\> tag as \<a\> tag.

Even for bots which could handle JavaScript, they won't 'CLICK' on your page, they just do rendering. If your link is not a \<a\> tag and depends on for example JavaScript history API, bots won't know them.

# next/Link vs Material-UI Link

For links in my [Grade Calculator](https://10converters.com/calculators/grade-calculator) page the links below is just rendered by Material-UI link:

```
import Link from '@material-ui/core/Link';

<Link href="/calculators/final-grade-calculator">
    Final Grade Calculator
</Link>
```

next/Link is generally implemented by a push history API, as we mentioned above, search engines doesn't like it.

Using @material-ui/core/Link components lost some of the benefits of Single Page App. for next/Link, relevant pages will be packed together, but if it's a @material-ui/core/Link used in Next.js app, linked pages(JavaScript sources) won't be packed together.

The con using @material-ui/core/Link is all resources have to be reloaded each time user navigate to a new page. Pro is the size of single page is smaller.

As this grade calculator is a small online tool, I assume people will not navigate to other pages. That's why I simply employed Link from Material-UI.

If you are in other cases, try to find some other better solutions.


# Error Page

Next.js has a /page/_error.js for error page handling. It works in server side rendering well. _error.js could get HTTP state code via getInitialProps().

In static HTML mode, like we said above in getInitialProps(), _error.js doesn't know what exact error is, is it a 404 or something else. But fortunately, because the app is static HTML, the only possible error is 404.

So always render 404 in _error.js is fine.

# NoSSR

If some part of your app really depends on runtime user requests, like URL parameter. use NoSSR for the page or for components on the page.

There're many NoSSR components available, I'm using Material-UI, so I use the NoSSR component comes with Material-UI.


# At The End

Even it is a very basic practice, I'd like to share the whole journey later about not only programming, but also deployment and many others. 

Enjoy.