Things I've Learned Building Next.js Apps

February 15, 2019 (5y ago)

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.

Read more about my thoughts on CSS Modules.

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,

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

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,


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.
  • As of Next.js 9, <Link> components will be automatically prefetched as they appear in-viewport. You no longer need to explicitly say prefetch.
  • As of Next.js 13, you no longer need to manually add a tags as a child of <Link> components.
<Link href="/">Home</Link>


Next.js supports 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.

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

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


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 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