Things I've Learned Building Next.js Apps

Lee Robinson

Lee Robinson / February 15, 2019

5 min read––– views

I've spent a lot of time in the past 4 months creating Next.js apps for both work and personal use.

Along that journey, I've grown to really ❤ Next.js and it's ecosystem. Here are some of the things I've learned along the way.

CSS Modules

I've worked with CSS, Sass, CSS-in-JS, and now CSS Modules. For most applications, I'd recommend CSS Modules.

CSS Modules prevent naming collisions and keep you from loading excess styling. They're built directly into Next.js, making it easy to get started.

For more information, check out How Should I Style My React Application?

Font Loading

By using @font-face in combination with the font-display attribute, I can prevent FOUT (Flash of Unstyled Text) and FOIT (Flash of Invisible Text).

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 100 900;
  font-display: optional;
  src: url(/fonts/inter-var-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
    U+FEFF, U+FFFD;
}

Further, I can preload my optimized font file in the Head of my document.

pages/_document.js
class MyDocument extends Document {
  render() {
    return (
      <Html lang="en">
        <Head>
          <link
            rel="preload"
            href="/fonts/inter-var-latin.woff2"
            as="font"
            type="font/woff2"
            crossOrigin="anonymous"
          />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

export default MyDocument;

Update: For more information, check out Web Fonts in 2021.

Dynamic Imports and Testing

It's likely that at some point in scaling your Next.js app, you'll want to use an external package that doesn't work well server-side rendered. For me, this package was react-select.

When SSR this component, it simply did not work in Safari. Until that bug is fixed, I needed a workaround. Thanks to Next's Dynamic Imports, it's easy to import a component and disable SSR.

import dynamic from 'next/dynamic';

const ReactSelectNoSSR = dynamic(() => import('../components/select'), {
  ssr: false
});

export default () => (
  <>
    <Header />
    <ReactSelectNoSSR />
    <Footer />
  </>
);

While this works, we can go a step further and provide a loading placeholder to make the user experience better.

const ReactSelectNoSSR = dynamic(() => import('../components/select'), {
  loading: () => <Input />,
  ssr: false
});

MDX

My portfolio has transformed quite a bit since I started it in 2014. Originally, it was just static HTML & CSS. Then, I switched over to Hugo so I could write my posts in Markdown. This worked well for a while, but I wanted full control over my layouts and felt much more comfortable with JavaScript. That's what led me to Next.js and MDX.

With MDX, I can use JSX components inside my Markdown documents with ease. This way, if I want to do something custom, it's as simple as importing a React component.

For maximum performance, you can use the prefetch attribute on Next's <Link> component. This will give the perception the page loads instantly. As of Next.js 8, prefetch uses <link rel="preload"> instead of a <script> tag. It also only starts prefetching after onload to allow the browser to manage resources.

<Link prefetch href="/">
  <a>Home</a>
</Link>

Update: Next.js 9 will automatically prefetch <Link> components as they appear in-viewport. You no longer need to explicitly say prefetch.

Polyfills

Next.js supports IE11 and all modern browsers (Edge, Firefox, Chrome, Safari, Opera) out of the box. However, it's possible that your own code or external NPM dependencies might use features not supported in your target browsers. In this case, you will need to add polyfills.

You'll need to add a top-level import for the specific polyfill you need in your Custom <App> or the individual component.

pages/_app.js

// Add your polyfills here or at the component level.
// For example...
// import 'resize-observer-polyfill'

function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />
}

export default MyApp

Vercel

Vercel is hands down the easiest way to deploy applications I've ever used. It's incredibly easy to get started and their GitHub integration will automatically deploy your app on pull requests and leave a link for you to review the changes. If everything looks good, it will deploy to prod when the PR is merged. Simple as that.

The icing on the cake is how well the Vercel ecosystem works together. Their domain service allows you to buy domains from the command line. I've never been able to go from an idea to a live, deployed application hosted on a domain so fast. I was even able to setup email forwarding for my domain and create me@leerob.io using improvmx 🎉

Update: I work at Vercel now. This was written before, but still holds true 😁

Where To Get Help

The Next.js and Vercel community support is fantastic. They're very responsive to emails, issues, or any other form of contact. My preferred method would be GitHub Discussions. Thanks to Discussions, all questions and answers are indexed, searchable, and easy to find. Their tutorials and docs are also fantastic and well-written.

Additional Resources

Subscribe to the newsletter

Get emails from me about web development, tech, and early access to new articles.

- subscribers – 31 issues