Skip to content
Swizec Teller - a geek with a hatswizec.com

Gatsby to NextJS pt1, server-side-render or server-side-generate?

Gatsby and NextJS do similar things differently. One is better for websites, the other for webapps. In episode 17 of CodeWithSwiz we explore the biggest difference.

CodeWithSwiz is a twice-a-week live show. Like a podcast with video and fun hacking. Focused on experiments. Join live Wednesdays and Sundays

Spring last year I had a flash of insight: Modern web apps are static first! It solves so many problems 🤯

Like time to first byte performance, loading spinners of death on bad wifi, and you can host from CDNs backed by a serverless data source. Works great.

Following that hunch I built Spark Joy on a series of streams. The app you see under every email and article.

Spark Joy feedback widgets in email
Spark Joy feedback widgets in email

Click a link, go to a page, answer followup questions. Works great.

Except when the email software says 15 readers clicked a vote and Spark Joy gets 5 votes 🤔

ServerSideRendering and ServerSideGeneration

When do you save that vote to the database?

With server side rendering your pages are created in advance. You deploy your site, go through the database, render a page for each relevant entry and turn it into HTML with data baked-in.

When users load a page, it hydrates and becomes a React app. You make requests in the browser and save the vote.

This is the approach Gatsby uses.

Server side rendering

You create pages like this in gatsby-node.js:

exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
const result = await graphql(`
query {
widgetsapi {
allWidget {
userId
widgetId
widgetType
followupQuestions
}
}
}
`)
result.data.widgetsapi.allWidget.forEach(
({ userId, widgetId, widgetType, followupQuestions }) => {
const votePath = path.resolve("./src/pages/vote.js")
createPage({
path: `/${widgetId}/thumbsup`,
component: votePath,
context: {
userId,
widgetId,
followupQuestions,
widgetType,
voteType: "thumbsup",
},
})
}
)
}

The pattern you'll see is:

  1. Fetch data with GraphQL
  2. Iterate over data
  3. createPage for each with a data-driven URL

Your page becomes static HTML served from a CDN. No servers. You read page params using the pageContext prop.

const VotePage = ({ pageContext }) => {
const {
userId,
widgetId,
voteType,
followupQuestions,
widgetType,
} = pageContext
return (
<FullScreen>
<SEO title="Thank You" />
<FormView
voteType={voteType}
onSubmit={onSubmit}
followupQuestions={followupQuestions}
widgetType={widgetType}
/>
</FullScreen>
)
}

Use props baked-in during deploy, render the page. No loading spinners.

Because this is a static page, you save votes on load. And that's how you lose them. Folks load the page and bail before the save request completes.

More on that next time ✌️

Server side generation

NextJS supports server side rendering and it's gonna have the same problem: What if users bail before data saves?

A "revolutionary" new approach that NextJS offers is server side generation. You'll recognize this as "How websites used to work 10 years ago".

With server side generation you generate the page on-demand. User makes a request, serverless lambda (not managed by you) wakes up, fetches data, builds the page. User gets static HTML, it hydrates into a React app.

getServerSideProps is how you turn that on:

export async function getServerSideProps({ params }) {
const { widgetId } = params
const { data } = await client.query({
query: gql`
query {
allWidget {
userId
widgetId
widgetType
followupQuestions
}
}
`,
})
// TODO: support a query to fetch this directly
const widget = data.allWidget.find((w) => w.widgetId === widgetId) || {}
return { props: { widget } }
}

Fetch data using GraphQL, return an object with props. Supports redirects and other fun stuff.

Where it differs from websites of old, is that your app uses the same code to render on the server and in the browser. Seamlessly.

const VotePage = ({ widget }) => {
const { userId, widgetId, voteType, followupQuestions, widgetType } = widget
return (
<Container sx={{ textAlign: "center" }}>
<Head>
<title>Thank you</title>
</Head>
<FormView
voteType={voteType}
onSubmit={onSubmit}
followupQuestions={followupQuestions}
widgetType={widgetType}
/>
</Container>
)
}

You get data from props and use it to render the page. No loading spinners.

And unlike with Gatsby, you have a chance to save data on every page load before the page loads. Force users to wait. We'll try that next time.

The SSR vs SSG tradeoff

SSR and SSG do the same thing: Serve a page with data baked-in.

The difference is when that happens. When you deploy your site (Gatsby), when the first user hits a page (NextJS), or when every user hits the page (NextJS)?

Each approach gives you different performance characteristics.

  1. Gatsby build-time rendering creates slow deploys and fantastic time to first byte for every user
  2. NextJS on-demand static pages create fast deploys, fantastic first load for most users, and terrible performance when cache is cold
  3. NextJS server side generation creates fast deploys, slow page loads, and a chance to do things pre-load

Which is best depends on what you're doing.

Cheers,
~Swizec

PS: Gatsby looks like it's moving towards SSG, they now support filename-based dynamic routing

Did you enjoy this article?

Published on November 19th, 2020 in CodeWithSwiz, Technical, Gatsby, NextJS, Livecoding

Learned something new?
Want to become a high value JavaScript expert?

Here's how it works 👇

Leave your email and I'll send you an Interactive Modern JavaScript Cheatsheet 📖right away. After that you'll get thoughtfully written emails every week about React, JavaScript, and your career. Lessons learned over my 20 years in the industry working with companies ranging from tiny startups to Fortune5 behemoths.

Start with an interactive cheatsheet 📖

Then get thoughtful letters 💌 on mindsets, tactics, and technical skills for your career.

"Man, love your simple writing! Yours is the only email I open from marketers and only blog that I give a fuck to read & scroll till the end. And wow always take away lessons with me. Inspiring! And very relatable. 👌"

~ Ashish Kumar

Join over 10,000 engineers just like you already improving their careers with my letters, workshops, courses, and talks. ✌️

Have a burning question that you think I can answer? I don't have all of the answers, but I have some! Hit me up on twitter or book a 30min ama for in-depth help.

Ready to Stop copy pasting D3 examples and create data visualizations of your own?  Learn how to build scalable dataviz components your whole team can understand with React for Data Visualization

Curious about Serverless and the modern backend? Check out Serverless Handbook, modern backend for the frontend engineer.

Ready to learn how it all fits together and build a modern webapp from scratch? Learn how to launch a webapp and make your first 💰 on the side with ServerlessReact.Dev

Want to brush up on your modern JavaScript syntax? Check out my interactive cheatsheet: es6cheatsheet.com

By the way, just in case no one has told you it yet today: I love and appreciate you for who you are ❤️