Next.js and Gatsby are modern front-end frameworks based on React.
Typically each is regarded as relevant for different use cases. There is a tendency to view Next.js as the go-to choice for large web applications with lots of dynamic content, while Gatsby is - to some extent correctly in my opinion - viewed as most beneficial for applications with small to medium content requirements.
In fact, both frameworks can be used to fit a massive range of use cases covering applications big and small. In this article, we'll be taking a look at how they handle the serving of HTML data i.e. rendering.
The advent of frontend libraries like React accompanied a surge in popularity of the Single-page application (SPA), which is an application that loads a single document and gradually modifies the body of that document in response to user interactions and navigation actions.
In the past, it was often the case that entire HTML documents had to be loaded from a remote web server whenever a user navigated to a new page. Thankfully this can be avoided nowadays using a SPA.
Next and Gatsby largely fit into the SPA paradigm, although they do enhance the functionality that can be achieved by using something basic like the create-react-app
CLI utility. Let's take a look at the three main types of rendering supported by the two of them.
Server-side rendering is a somewhat advanced technique whereby a fully formed HTML document is sent to the client's browser, often providing a better experience by minimizing the perceived loading time. On the other hand, it can increase First Input Delay, which is the time before the page can be interacted with.
Other advantages of this rendering technique include better SEO performance, loader perceived load time (no blank screen as is common with CSR), and more efficient data fetching. If your service relies heavily on social media sharing to drive traffic, SSR is hugely beneficial because of its ability to populate Open Graph and Twitter Cards with rich, dynamic data.
In Next, SSR can be implemented using the getServerSideProps
method, as seen in the snippet below. This method has access to the initial network request, which can make it useful for loading different data depending on, among other things, the request locale, language or device type.
// _app.tsx
MyProject.getServerSideProps = async (context: AppContext) => {
const session = await getSession(context.ctx);
return {
props: {
session,
},
};
};
Any file listed in the pages
directory of a Next project can have a getServerSideProps
method. What this method does on a more technical level is define a snippet of JavaScript or TypeScript code that will be run in a serverless function in a Node.js environment - see the Next on Netlify plugin for how to do this easily.
Whatever props are returned by getServerSideProps
will be injected into the page component on the server, which will in turn render a full HTML document to be sent over the wire to the browser.
In spite of its reputation as a static site generator, Gatsby is also capable of performing server-side rendering.
In Gatsby, the getServerData
method can be used to indicate that we want a particular page to use server-side rendering. This might look something like the snippet below:
// my-page.tsx
export async function getServerData(
props: GetServerDataProps
): GetServerDataReturn<ServerDataProps> {
return {
status: 200,
headers: {},
props: {
message: "Hello World",
},
}
}
Like in Next with getServerSideProps
, the getServerData
function must be executed in a Node.js environment in order to work. Thankfully there is a a Netlify plugin designed to do this without us needing to provision our own infrastructure.
Gatsby Cloud also offers automatic setup of SSR as part of its plans, so that is a good option too.
Note on SSR Though SSR can be a very powerful tool, it might make it harder to develop and maintain your application code in future since not all developers will be used to it. There may also be additional hosting costs if the server-side code requires additional infrastructure to run on, even though on a service like Netlify the cost will grow linearly with network requests.
Static Site Generation is a technique whereby all or part of a website is generated in advance, typically in a Node.js runtime.
What is great about this is how fast it makes the resultant website. Because all the HTML, CSS and JS has been precompiled into individual bundles for each page, a change in route in the client's browser only requires the browser to load in the bundle for that route, leading to blazingly fast page loading.
As a result, SSG is a great choice if you have small to medium content requirements and know that your content is unlikely to change often.
Assuming you have certain content that changes according to the user's authenticated state (logged in vs logged out), it may be advisable to use SSG with caution, as rehydration problems can occur if the authenticated state is only known after client-side running of JavaScript, a problem described here and one which can also affect SSR.
Next.js implements SSG through a function called getStaticProps
, which might typically look something like the snippet below:
// my-static-page.tsx
export const getStaticProps: GetStaticProps = async (context) => {
const pageData = await loadDataFromServer(context);
return {
props: {
pageData,
},
}
}
Another useful function in the same mold as this is getStaticPaths
, which can be used to predefine all the possible routes for a specific type of path. To clarify, say we had a few hundred different articles on a news website, we could define a page called articles/[id].tsx
, and getStaticPaths
would prerender a list of articles defined within its method body.
If SSG is only required on a subset of, say, the most popular pages, the fallback: true
option can be added to getStaticPaths
, which will make builds faster, albeit at the expense of an initially slower load time.
As Gatsby is pretty much synonymous with SSG, it's no surprise that it's been built to work incredibly well with this type of rendering.
On any given Gatsby page, you can define a pageQuery
that can load data and populate the page. Generally speaking, Gatsby is geared towards using a headless CMS like Contentful. Data transfer is oriented around GraphQL, which is particularly efficient as it allows developers to load only the data that is genuinely required.
export const pageQuery = graphql`
query GetBlogPostBySlug($slug: String!) {
site {
siteMetadata {
title
siteUrl
}
}
contentfulBlogPost(slug: { eq: $slug }) {
title
slug
publishedAt
tags
}
}
`
In the example above, we are loading a blog article from the Contentful CMS by using its slug, which is passed to the GraphQL query at build time.
For configuring what happens at build time, which is when the static generation takes place, we can use the gatsby-node.js
file:
const path = require("path");
exports.createPages = ({ graphql, actions }) => {
const { createPage } = actions;
return new Promise((resolve, reject) => {
const postTemplate = path.resolve("src/templates/blogpost-template.js");
resolve(
graphql(
`
{
allContentfulBlogPost {
edges {
node {
title
slug
}
}
}
}
`
).then(({ errors }) => {
if (errors) {
console.log(errors);
reject(errors);
}
const posts = result.data.allContentfulBlogPost.edges;
posts.forEach((post) => {
createPage({
path: `/blog/${post.node.slug}/`,
component: postTemplate,
context: {
slug: post.node.slug,
},
})
})
})
)
})
}
In the example above, we are loading blog posts using the Contentful GraphQL API and using the createPage
API to inject the slug of each blog post into an HTML-CSS-JS template which will use the slug to load the post details using the pageQuery
we saw earlier.
Although Gatsby makes SSG very enjoyable to work with, the learning curve is somewhat steep as it requires developers to be reasonably competent at GraphQL.
Nonetheless, even this latter hurdle can be overcome using the /_graphql
endpoint available in development mode; this includes a Code Exporter
feature attached to GraphiQL, which makes query formulation much easier for those without GraphQL knowledge.
Note on SSG As much as SSG might seem like the answer to a lot of common web development problems, notably slow page load, powering a large-scale application with SSG alone may lead to inordinately long build times, potentially incurring a large cost. Techniques like Gatsby's Deferred Static Generation can mitigate this by configuring certain pages to only be statically generated - and subsequently cached - at the time of the first user request.
Client-side rendering will be familiar to most React developers because it's the default for any project created with the widely used create-react-app
.
What CSR means is that the browser is responsible for creating the entire HTML document that users see. Although this can result in seemingly faster page transitions compared to SSR, it's not uncommon to see a flash of a blank page or interminable loading spinner animations while the browser puts together the required HTML.
In Next.js, CSR is the default configuration. Just like in a typical SPA created through create-react-app
, you can use lifecycle hooks like useEffect
to load data from a remote server.
Next's creator Vercel makes things even easier for us in this regard by providing the useSWR hook that deals with caching, periodic refetches, revalidations and more.
CSR in Gatsby can be implemented using client-only routes. Since Gatsby ships with @reach/router
, developers can define routes in a way that will feel very familiar to anyone who has worked on a project created using create-react-app
.
Although CSR definitely has its place in Gatsby, it can seem like we're underutilizing Gatsby's rich functionality if we rely too heavily on this rendering mode.
Note on CSR As the simplest one of the three rendering modes to learn, CSR is a good choice if speed is your primary business goal. That said, its convenience does come with obvious disadvantages. SEO will be severely impacted because search engine crawlers are not always capable of acquiring knowledge client-side rendered pages. Similarly, social media and external link sharing will be ineffectie since OpenGraph and Twitter cards can't pull metadata from applications using CSR.
This article compared the different ways Gatsby and Next.js approach data loading and rendering.
We saw that both can used for server-side rendering, client-side rendering and static site generation, also known as server-side generation.
Undoubtedly there is a place for each of these three rendering modes in web development, and we're lucky that in the modern era there are frameworks like these that make things easier than ever for us.
Thanks for reading!
Also Published Here