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
7 min read • Loading views
Why Switching to Next.js?
- Level up myself
- Flexibility in choosing page rendering method
- Implement API services with ease
The Stack
- Next.js
- TailwindCSS
- MDX
- Firestore
- SWR
- Vercel
- sal.js
How's the Process?
Components
All components can stay the same as they were. I only replaced all of the Gatsby Image and Link components to Next.
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 another way of doing it in Gatsby. Anyways, I had 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:
module.exports = {
//...
darkMode: 'class'
}
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:
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.
I had to write a custom scripts to generate sitemap and RSS Feed. 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.
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 customizations. 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.
Edited: TailwindCSS 3.0 finally added a
not-prose
class option for us to achieve the same results!
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.
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:
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 });
}
}
Another minor improvement would be adding sal.js for scroll animations. It's a lightweight library that utilizes the IntersectionObserver API.
Regarding performance, I got almost perfect Google Lighthouse scores. 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.
What I've Learnt
Gatsby vs Next Image
I like that Gatsby has built-in support of image placeholder for all static and dynamic images while you have to implement the blur placeholder yourself with third-party libraries in Next. I used plaiceholder
to generate base64 blurred images.
But I do understand while Next skips handling dynamic images means a shorter build time.
TailwindCSS Prettier Plugin
Styling with TailwindCSS can make your className prop messy. I found a Prettier plugin which helps sort all your Tailwind classes. You have to use it.
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 also need to learn GraphQL and Gatsby's lifecycle in order to start doing bits of customization.
Both Next and Gatsby are opinionated frameworks. But Next is less-opinionated comparatively. Next mainly hides away all the routing and bundling part. Next can minimize all setup hassels and let us focus on the core product. 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 API 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!
Developer Experience is way better in Next
Gatsby occasionally breaks when it recompile my MDX file changes. Having to use next-remote-watch
for writing irons out the process. 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 every time 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!