Next.js - A React starter kit you can stick with
Tags: webdev, react, nextjs, javascript
Published: 2021-01-18 18:34:33

I've tried just about all the popular react starter kits.  In 2016 the first npm package I ever installed to begin learning react was `create-react-app`. After ejecting the package my first time, I then quickly moved on to creating my own custom react starter kit, but quickly realized I had no intention of maintaining it. From there I built toy projects using Gatsby, react-redux-starter-kit,  and the list goes on.

While I could compare Next.js to both Gatsby and Create React App, for brevity and showcasing its power today I'm choosing to pick on Create React App (CRA).

## Picking on CRA

I've never used create-react-app without eventually needing to run "eject". When you do, you're met with a consortium of various configurations for webpack, babel, eslint, jest, and other tools, many of which you are not sure if you'll need. But this process leaves me feeling somewhat afraid about what is okay to remove. Once you do run "eject" you are completely on your own. So you get the choice between having a project which has one dependency, `create-react-app` which will conceal the bloat. Or owning all of the dependencies it conceals and managing the bloat yourself.

![Angry face](https://media.giphy.com/media/R54jhpzpARmVy/giphy.gif)
This process often leads me to throw my hands up in the air and saying "well fine, I'll just make my own simple webpack config for this small project I'm working on". CRA is inherently opinionated. I should be clear, I don't think that's necessarily a bad thing *(especially if you're just barely getting into React and need to hit the ground running quickly)*. If you like the out of the box tooling CRA ships with then you should have pretty smooth sailing. 

## My Needs

1. For context, I decided to revamp my portfolio site. I wanted to do this quickly, in a week or less. It didn't make sense to tinker with client configs for days on end getting things "just perfect" only to discover once my project was out in the wild that it was sorely unoptimized. So a bootstrapper was needed and made sense.
2. I wanted a starter kit that was testing agnostic. Jest is great and has come a long way over the last few years, but I still prefer to use something on top of Jest like `react-testing-library`, paired with some smokescreen e2e tests written in Cypress. Because of this need, I would prefer to manage the test setup code completely on my own.
3. I wanted a lot of control over the client-side libraries I chose to use and needed something flexible.
4. Typescript. I love TS what more can I say. While CRA *does* include support for Typescript, the support isn't the best. You have to initialize CRA with a custom template command. Afterward, CRA then attempts to use Babel and Typescript together, which has some interesting side effects, like for example, no enum or namespace support.
5. I don't want to ever have to worry about being involved with a process like "ejecting". I want a client-side starter kit that will shallowly allow for me to disagree on some things, and use my own customizations for things one would expect.
6. For most of my side projects, like my portfolio site, a completely separate server written in Express, GraphQL, etc is just way overkill. Some level of serverside support goes a **long way** for a project like this, so I can write some simple light-weight API endpoints as a cherry on top.

## Enter Next.js

1. The first magical moment with Next.js was the second I wanted to add Typescript to the project. The Next.js philosophy is "no config needed". And that's what they try and do. Stay out of your way while you build. I followed the short setup guide for TS, and it was one of those apple moments where "it just worked ™". I didn't have to worry about what webpack was doing under the hood, or concern myself with any constraints on Typescript *(like enums and namespaces not working in CRA)*.
2. The second magic moment was how Next.js handled clientside routing. The `next/Link` component can be used anywhere and will handle any and all internal linking that you want to do. React components placed in the `/pages` directory are automatically registered as a route based on the naming convention of the file. Non-page components *(like reusable, shared components)* can be placed outside of the `/pages` directory in a directory of your choice to avoid exposing them as a route.
3. The API layer. Inside of the `/pages/api` directory Next.js includes support for writing your own thin Next endpoints. In my case, a paper-thin client API was all I needed so this worked great. As the project grows they have support for adding custom middlewares, following the express/koa-like syntax of using function callbacks that contain a `(request, response, next)` argument convention. In addition, there is no need to worry about cross-origin requests as the same server which serves up the client also serves up the client API. Vercel also owns the [SWR](https://www.npmjs.com/package/swr) package, which they recommend pairing with the Client API. SWR follows the `stale-while-revalidate` cache invalidation strategy through easy to use react hooks which assist in client request caching.
4. The areas where Next lacks in opinion was refreshing. I threw together my own testing suite and didn't have to worry about magical test frameworks behind the scenes stepping on my toes. I began adding font libs, animation libs, prettier, eslint.
5. Secret sharing. Next.js has a built-in mechanism for sharing secrets. It's a fairly standard procedure here. Environment variables specified in a `.env.local` are attached to `process.env` and can be used on both the client/server in that manner.

## When You Need To Go Beyond
Vercel seems to understand that eventually you may just wanna dip out from their defaults and try your own thing.  When that happens, rather than "ejecting" from next, you can specify some of the traditional build flows in a webpack config through using the `next.config.js` file. Vercel's docs include examples of swapping out the defaulted CSS Modules setup with JSS, or Sass, as well as adding PostCSS support and more.

## Benefits

I won't go too deep on all of the benefits Next.js promises. But there is a whole slew of features that Vercel has packed in here.

- Pre-rendering components leads to better performance and SEO.
- Fast Refresh feels like webpack hot-reloading decided to start taking performance enhancement drugs.
- Image serving/caching is a really fantastic experience using the `next/Image` component.
- Vercel's deployment platform was built specifically for paired use with Next.js, and has a really compelling free tier. The two when used together provided yet another "magic moment" for me.
- To learn more I recommend looking at [Why Next.js].(https://nextjs.org/)

## Caveats & Dislikes

While I believe Next.js is a great tool for my needs, no npm package ever deployed is a silver bullet. There are some downsides, and admittedly a few strong opinions that Next.js has.

1. **JSS**. Next ships with default support for CSS Modules. While there is nothing stopping you from going your own way,  there is one technical constraint put on using JSS. Namely that you cannot use it in server-side components. Decisions like this do push the codebase toward the direction of using CSS Modules and being okay with it. Ultimately this tradeoff didn't impact me, but that may not be the case for you.
2. **TS Config**, if you change it, Next.JS will just re-generate the config back to what it's expecting. This isn't all the way bad though, as the standard TS Config is pretty flexible for most project needs. I wanted to make my compiler a bit more strict, and that was where I started running into issues. There are workarounds *(like using good eslint rules which TS plugins)*. I ended up using the following which I was happy with:

```json
"extends": [
    "airbnb",
    "plugin:cypress/recommended",
    "plugin:react/recommended",
    "plugin:react/recommended",
    "plugin:@typescript-eslint/recommended",
    "prettier/@typescript-eslint",
    "plugin:prettier/recommended"
  ],
```

## TLDR;

I've never moved faster on a side project than when I picked up Next.js. It got me out of the boilerplate and into writing the more interesting parts of my codebase fast. The package was able to do this in such a way that I still felt like I was in control and never had to worry about any process like "ejecting". I'm completely won over, and moving forward I would have to look for good reasons not to use Next.js on any new front end project that comes my way.