Coding

How I Migrated My Website from Gatsby to Next

I migrated my personal site to Next.js and I'm not going back!

By Fabian Lee | July 14, 2021

6 min readLoading views

How I Migrated My Website from Gatsby to Next

Why Switching to Next.js?

  1. Get involve in more backend development and services
  2. Flexibility in choosing page rendering method
  3. Experience hands-on Next.js development

The Stack

  • Next.js
  • TailwindCSS
  • MDX
  • Firestore
  • SWR
  • Vercel
  • Framer Motion (Haven't add it back to here)

How's the Process?

Copying and Replacing Components

Static pages and global styles are mostly done by copy and pasting them into Next. Then I have to replace all of the Gatsby Image and Link components to Next's version. I have fewer individual components than before for the sake of time.

Dark Mode

I had to hack a script in html.js and create my own ThemeProvider to get it working smoothly in Gatsby. Josh Comeau has other way of doing it in Gatsby. Anyways, we have to add a significant amount of code to achieve a simple UI change.

next-theme made it so easy and it works perfectly with TailwindCSS simply by setting the attribute prop in its ThemeProvider that match with the dark mode settings in tailwind.config.js.

Here's the example:

tailwind.config.js
module.exports = {
  //...
  darkMode: 'class'
}

_app.js
export default function App({ Component, pageProps }) {
  return (
    //  Tells next-theme to toggle the class attribute in the root html
    <ThemeProvider attribute="class">
      <MDXProvider components={MDXComponents}>
        <Component {...pageProps} />
      </MDXProvider>
    </ThemeProvider>
  );
}

Replacing Gatsby Plugins

Gatsby plugins are sort of like Wordpress plugins where you install what features you need. Next is fundamentally a Node application so I had to install individual libraries to set them up explicitly.

gatsby-source-* souring plugins became API fetches or fs reads in getStaticProps to retrieve all the content.

Here's an example:

pages/index.js
const Index = ({ blog, bookNotes }) => {
  // Map through 2 arrays to list out the latest 3 posts
}

// Runs at build time
export const getStaticProps = async () => {
  const blog = await getAllFilesFrontMatter('blog').slice(0, 3);
  const bookNotes = await getAllFilesFrontMatter('book-notes').slice(0, 3);
  return { props: { blog, bookNotes } };
};

gatsby-plugin-mdx along with a bunch of remark plugins are replaced by next-mdx-remote, gray-matter and original remark libraries.

// Parses the mdx file get the content and frontmatter
const { data, content } = matter(source);
// Further parses the content with remark plugins
const mdxSource = await serialize(content, {
  mdxOptions: {
    remarkPlugins: [
      require('remark-slug'),
      [
        require('remark-autolink-headings'),
        {
          linkProperties: {
            className: ['anchor'],
          },
        },
      ],
      require('remark-code-titles'),
    ],
    rehypePlugins: [mdxPrism],
  },
});

Alias import and Head component are provided by Next out of the box. Nothing special needs to be installed.

Sitemap and RSS Feed had to be generated through custom scripts. The basic idea is just reading all the page files and map them to the corresponding XML format. And I had to add robots.txt manually.

MDX Posts Styles

I did all the MDX posts styling manually and pass them to MDXProvider in Gatsby.

MDXComponents.js
export default {
  h1: (props) => <h1 className="text-5xl font-bold mb-8 mt-12" {...props} />,
  h2: (props) => <h2 className="text-4xl font-bold mb-7 mt-11" {...props} />,
  h3: (props) => <h3 className="text-3xl font-bold mb-6 mt-10" {...props} />,
  h4: (props) => <h4 className="text-2xl font-bold mb-5 mt-9" {...props} />,
  h5: (props) => <h5 className="text-xl font-bold mb-4 mt-8" {...props} />,
  h6: (props) => <h6 className="text-lg font-bold mb-3 mt-7" {...props} />,
  p: (props) => <p className="mb-8 leading-7" {...props} />,
  a: (props) => <Link {...props} />,
  ul: (props) => <ul className="list-disc pl-4 mb-8" {...props} />,
  ol: (props) => <ol className="list-decimal pl-5 mb-8" {...props} />,
  li: (props) => <li className="leading-7 mb-3 pl-1" {...props} />,
  inlineCode: (props) => <Code {...props} />,
  blockquote: (props) => <Blockquote {...props} />,
};

Glad I found @tailwindcss/typography to help me skip all these customisations. With some extra configuration for dark mode styles, I simply needed to add prose class to style my posts automatically. Though it comes with a drawback of overriding all your custom components styles inside any MDX files. I had to add an unprose class to disable some styles.

Deployment

I moved from Netlify to Vercel simply because Next is backed by Vercel which should have the best optimization. They both have auto preview and production deployment when you make a pull request and merging into your main branch. Netlify requires you to install a plugin to enable Next's dynamic features like dynamic routes and APIs though.

Enhancements

I added a page views feature for my posts using Firebase Firestore. And I use swr to do client-side data fetching.

Views.js
const Views = ({ slug }) => {
  const { data } = useSWR(`/api/views/${slug}`, fetcher);
  const views = new Number(data.total);
  return <p>{`${views} views`}</p>;
}

API route for getting the views from Firestore:

pages/api/[slug].js
export default async function handler(req, res) {
  if (req.method === 'GET') {
    const postRef = await db.collection('posts').doc(req.query.slug);
    const doc = await postRef.get();
    const data = doc.data();

    return res.status(200).json({ total: data?.views || 0 });
  }
}

I got almost perfect Google Lighthouse score. Next enforces certain rules with images that helped my Cumulative Layout Shift (CLS) performance and accessibility. Gatsby was great too overall. It's my OCD kicking in to push those numbers up.

Lighthouse Score
Lighthouse Score

What I Learnt

Gatsby vs Next Image

I like that Gatsby has built-in support of image placeholder while you have to implement the blur placeholder yourself with third-party libraries in Next. I used plaiceholder to generate base64 blurred images.

Less Blackbox-like with Next

Gatsby can get your static site up and running fast combined with different plugins. The ecosystem tends to hide all low level settings away. You will need to learn GraphQL and Gatsby's lifecycle in order to start doing bits of customization.

Next is less-opininated framework even though it hides away all the routing and bundling part. These abstractions make sense because they are not related to the product or the content itself. Next can minimize these hassels and let us focus on the core. With your React and Node knowledge, you can basically do whatever and however you want in Next.

Content Security Policy

It's a shame that I haven't looked into CSP in Gatsby. Security wasn't in my checklist. It's an essential topic to secure my site since I'm implementing backend services. You can test out your site's security here. You should see what security measures you are missing. Use the report as a reference and setup your CSP accordingly. Ace it now!

Security Report Summary
Security Report Summary

Developer Experience is way better in Next

Gatsby occassionally breaks when it recomplies my MDX file changes. Having to use next-remote-watch for writing is much smoother and faster. The build and deployment time are also significantly faster in Next.

Even if I have thousands of posts, I can always use the fallback: true option in getStaticPaths to generate posts on the fly without having to pre-render all of them everytime I make a change to my site.

I spent less time reading documentations in Next than in Gatsby. As I said before, React and Node knowledge are primarily all you need to start building!

Thanks For Reading 😉

If you would like to show your further support:

Buy Me a Book