Differences Between Static Generated Sites And Server-Side Rendered Apps

About The Author

Front-end developer based in Lagos, Nigeria. He enjoys converting designs into code and building things for the web. More about Timi ↬

Email Newsletter

Weekly tips on front-end & UX.
Trusted by 200,000+ folks.

Statically generated sites or pre-rendering and server-side rendered applications are two modern ways to build front-end applications using JavaScript frameworks. These two modes, yet different, are often mixed up as the same thing and in this tutorial, we’re going to learn about the differences between them.

JavaScript currently enables you to build three types of applications: single-page applications (SPAs), pre-rendered or static-generated sites, and server-side-rendered applications. SPAs come with many challenges, one of which is search engine optimization (SEO). Possible solutions are to make use of a static-site generator or server-side rendering (SSR).

In this article, we’ll go over these, looking at their pros and cons to get a balanced view. We’ll look at what static generation is, as well as frameworks that help us create static-generated sites, such as Gatsby and VuePress. We’ll learn what a server-side-rendered application is, as well as learn about frameworks for creating one, such as Next.js and Nuxt.js. Finally, we’ll cover the differences between these two methods and see which you should use to build your next application.

You can find all of the code snippets in this article on GitHub.

What Is a Static-Site Generator?

A static-site generator (SSG) is a software application that creates HTML pages from templates or components and a given content source. Give it some text files and content, and the generator will give you back a complete website; this completed website is referred to as a static-generated site. This means that the website’s pages are generated at build time, and their contents do not change unless you add new content or components and then rebuild — you have to rebuild the website if you want it to be updated with the new content.

Diagram explaining how static-site generation works
How static-site generation works (Large preview)

This approach is good for building applications whose content does not change often. So, you wouldn’t necessarily use it for a website that has to be modified according to the user or one that has a lot of user-generated content. However, a blog or personal website would be an ideal use. Let’s look at some advantages of static-generated sites.

Pros

  • Speed
    Because all of your website’s pages and content will be generated at build time, you do not have to worry about API calls to a server for content, which will make your website very fast.
  • Deployment
    Once your static site has been generated, you will be left with static files. Hence, it can be easily deployed to a platform such as Netlify.
  • Security
    A static-generated site solely comprises static files, with no database for an attacker to exploit by injecting malicious code. So, vulnerability to a cyber attack is minimal.
  • Verson control
    You can use version control software (such as Git) to manage and track changes to your content. This comes in handy when you want to roll back changes made to the content.

Cons

  • If the content changes too quickly, it can be hard to keep up.
  • To update content, you have to rebuild the website.
  • The build time increases according to the size of the application.

Examples of static-site generators are Gatsby and VuePress. Let’s look at how to create a static site using these two generators.

Gatsby

According to the official website:

“Gatsby is a free and open-source framework based on React that helps developers build blazing-fast websites and apps.”

This means that developers who are familiar with React will find it easy to get started with Gatsby.

To use this generator, you first have to install it using npm:

npm install -g gatsby-cli

This will install Gatsby globally on your machine. You have to run this command only once on your machine. Once the installation is complete, you can create your first static site using the following command:

gatsby new demo-gatsby

This will create a new Gatsby project, which I have named demo-gatsby. Following this, you can start up your app’s server by running the following command:

cd demo-gatsby
gatsby develop

Your Gatsby application should be running on localhost:8000.

Gatsby default landing page
Gatsby default starter page (Large preview)

The folder structure of the app looks like this:

--| gatsby-browser.js  
--| LICENSE        
--| README.md
--| gatsby-config.js
--| node_modules/  
--| src/
----| components
----| pages
----| images
--| gatsby-node.js     
--| package.json   
--| yarn.lock
--| gatsby-ssr.js      
--| public/
----| icons
----| page-data
----| static

For this tutorial, we’re only going to look at the src/pages folder. This folder contains files that will be generated into routes on the website.

To test this, add a new file (newPage.js) to this folder:

import React from "react"
import { Link } from "gatsby"
import Layout from "../components/layout"
import SEO from "../components/seo"
const NewPage = () => (
  <Layout>
    <SEO title="My new page" />
    <h1>Hello Gatsby</h1>
    <p>This is my first Gatsby page</p>
    <button>
      <Link to='/'>Home</Link>
    </button>
  </Layout>
)
export default NewPage

Here, we are importing React from the react package, so when your code is transpiled to pure JavaScript, references to React will appear there. We’re also importing a Link component from gatsby; it is one of React’s route tags that is used in place of the native anchor tag (<a href="#">Link</a>). It accepts a to prop, which takes a route as a value.

We imported a Layout component, which was added to the app by default. This component handles the layout of pages nested inside it. We also imported the SEO component into this new file. This component accepts a title prop and configures this value as part of your page’s meta data. Finally, we exported the function NewPage, which returns JSX containing the new page’s content.

In your index.js file, add a link to this new page we just created:

import React from "react"
import { Link } from "gatsby"
import Layout from "../components/layout"
import Image from "../components/image"
import SEO from "../components/seo"
const IndexPage = () => (
  <Layout>
    <SEO title="Home" />
    <h1>Hi people</h1>
    <p>Welcome to your new Gatsby site.</p>
    <p>Now go build something great.</p>
    <div style={{ maxWidth: `300px`, marginBottom: `1.45rem` }}>
      <Image />
    </div>
    <Link to="/page-2/">Go to page 2</Link>
    {/* new link */}
    <button>
      <Link to="/newPage/">Go to new page</Link>
    </button>
  </Layout>
)
export default IndexPage

Here, we’ve imported the same components that were used in the newPage.js file, and they perform the same function in this file. We’ve also imported an Image component from our components folder. This component is added by default to the Gatsby application; it helps with lazy loading images and serving lower file sizes. Finally, we’ve exported the IndexPage function, which returns JSX containing our new link and some default content.

Now, if we open the browser, we should see our new link at the bottom of the page.

Gatsby default landing page with link to new page
Gatsby landing page with new link (Large preview)

And if you click on “Go to new page”, it should take you to your newly added page.

New page containing some text
New Gatsby page (Large preview)

VuePress

VuePress is a static-site generator powered by Vue.js, vue-router, and webpack. It requires little to no configuration to get started. While there are many static-site generators, VuePress stands out from the pack for a single reason: Its primary purpose is to make it easier for developers to create and maintain great documentation for their projects.

To use VuePress, you first have to install it:

// Globally…
yarn global add vuepress # OR npm install -g vuepress

// Or in an existing project…
yarn add -D vuepress # OR npm install -D vuepress

Once the installation process is done, you can run the following command in your command-line interface (CLI):

# Create the project folder
mkdir demo-vuepress && cd demo-vuepress

# Create a Markdown file
echo '# Hello VuePress' > README.md

# Start writing
vuepress dev

Here, we’ve created a folder for our VuePress application, added a README.md file with # Hello VuePress as its only content, and, finally, started up the server.

Once this is done, our application should be running on localhost:8080, and we should see this in the browser:

A VuePress web page with text saying “Hello VuePress”
VuePress landing page (Large preview)

VuePress supports Vue.js’ syntax and markup in this file. Update the README.md file with the following:

# Hello VuePress
_VuePress Rocks_
> **Yes!**
_It supports JavaScript interpolation code_
> **{{new Date()}}**
<p v-for="i of ['v','u', 'e', 'p', 'r', 'e', 's', 's']">{{i}}</p>

If you go back to the browser, the page should look like this:

Updated VuePress page
Updated VuePress page (Large preview)

To add a new page to your VuePress website, add a new Markdown file to the root directory, and name it whatever you want the route to be. In this case, I’ve gone ahead with naming it Page-2.md and added the following to the file:

# Hello World
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat.

Now, if you navigate to /page-2 in the browser, you should see this:

A VuePress web page containing “Hello world”
A “Hello World” page in VuePress (Large preview)

What Is Server-Side Rendering?

Server-side rendering (SSR) is the process of rendering web pages on a server and passing them to the browser (client-side), instead of rendering them in the browser. SSR sends a fully rendered page to the client; the client’s JavaScript bundle takes over and enables the SPA framework to operate.

This means that if your application is server-side rendered, the content is fetched from the server and passed to the browser to be displayed to the user. Client-side rendering is different: The user would have to navigate to the page before the browser fetches data from the server, meaning that the user would have to wait for some seconds to pass before the browser is served with the content for that page. Applications that have SSR enabled are called server-side-rendered applications.

A diagram explaining how server-side rendering works
How server-side rendering works (Large preview)

This approach is good if you’re building a complex application that requires user interaction, that relies on a database, or whose content changes often. If the content changes often, then users would need to see the updates right away. The approach is also good for applications that tailor content according to who is viewing it and that store user data such as email addresses and user preferences, while also attending to SEO. An example would be a large e-commerce or social media platform. Let’s look at some of the advantages of SSR for your applications.

Pros

  • The content is up to date because it is fetched on the go.
  • The website loads quickly because the browser fetches content from the server before rendering it for the user.
  • Because the JavaScript is rendered server-side, the user’s device has little bearing on the loading time of the page, making for better performance.

Cons

  • More API calls to the server are made, because they’re made per request.
  • The website cannot be deployed to a static content delivery network (CDN).

Examples of frameworks that offer SSR are Next.js and Nuxt.js.

Next.js

Next.js is a React framework that enables you to build static websites, server-side rendered applications, and the like. Because it is built on React, knowledge of React is required to use the framework.

To create a Next.js app, run the following:

npm init next-app
# or
yarn create next-app

You will be prompted to name your application; I named mine demo-next. The next option is to select a template; I selected the default starter app. Then, it begins to set up the application. When that is done, we can start our work on the application.

cd demo-next
yarn dev
# or npm run dev

Your application should be running on localhost:3000, and you should see this in the browser:

Default Next.js landing page
Next.js landing page (Large preview)

The page that is being rendered can be found in pages/index.js. So, if you open this file and modify the JSX inside the Home function, it would be reflected in the browser. Replace the JSX with this:

import Head from 'next/head'
export default function Home() {
  return (
    <div className="container">
      <Head>
        <title>Hello Next.js</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <main>
        <h1 className="title">
          Welcome to <a href="https://nextjs.org">Next.js!</a>
        </h1>
        <p className='description'>Next.js Rocks!</p>
      </main>
      <style jsx>{`
        main {
          padding: 5rem 0;
          flex: 1;
          display: flex;
          flex-direction: column;
          justify-content: center;
          align-items: center;
        }
        .title a {
          color: #0070f3;
          text-decoration: none;
        }
        .title a:hover,
        .title a:focus,
        .title a:active {
          text-decoration: underline;
        }
        .title {
          margin: 0;
          line-height: 1.15;
          font-size: 4rem;
        }
        .title,
        .description {
          text-align: center;
        }
        .description {
          line-height: 1.5;
          font-size: 1.5rem;
        }
      `}</style>
      <style jsx global>{`
        html,
        body {
          padding: 0;
          margin: 0;
          font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto,
            Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue,
            sans-serif;
        }
        * {
          box-sizing: border-box;
        }
      \`}</style>
    </div>
  )
}

In this file, we’re making use of Next.js’ Head component to set the meta data’s title and favicon for our page. We’re also exporting a Home function that returns JSX containing our page’s content. This JSX contains the Head component, together with the page’s main content. It also contains two style tags, one for styling this page and the other for global styling of the app.

Now, you should see that the content in the app has changed to this:

Next.js landing page containing “Welcome to Next.js” text
Updated landing page (Large preview)

If we wanted to add a page to our app, we would have to add a file in the /pages folder. Routes are automatically created based on the /pages folder structure. Suppose you have a folder structure that looks like this:

--| pages
----| index.js ==> '/'
----| about.js ==> '/about'
----| projects
------| next.js ==> '/projects/next'

In the pages folder, add a new file and name it hello.js. Then add the following to it:

import Head from 'next/head'
export default function Hello() {
  return (
    <div>
       <Head>
        <title>Hello World</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <main className='container'>
        <h1 className='title'>
         Hello <a href="https://en.wikipedia.org/wiki/Hello_World_(film)">world</a>
        </h1>
        <p className='subtitle'>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Voluptatem provident soluta, sit explicabo impedit nobis accusantium? Nihil beatae, accusamus modi assumenda, optio omnis aliquid nobis magnam facilis ipsam eum saepe!</p>
      </main>
      <style jsx> {`

      .container {
        margin: 0 auto;
        min-height: 100vh;
        max-width: 800px;
        text-align: center;
      }
      .title {
        font-family: "Quicksand", "Source Sans Pro", -apple-system, BlinkMacSystemFont,
          "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
        display: block;
        font-weight: 300;
        font-size: 100px;
        color: #35495e;
        letter-spacing: 1px;
      }
      .subtitle {
        font-weight: 300;
        font-size: 22px;
        color: #526488;
        word-spacing: 5px;
        padding-bottom: 15px;
      }
      \`} </style>
    </div>
  )
}

This page is identical to the landing page we already have. We’ve only changed the content and added new styling to the JSX. Now, if we visit localhost:3000/hello, we should see our new page:

A Next.js web page containing “Hello world”
A “Hello World” page in Next.js (Large preview)

Finally, we need to add a link to this new page on the index.js page. To do this, we’ll make use of Next.js’ Link component. We have to import it first.

# index.js
import Link from 'next/link'

#Add this to your JSX
<Link href='/hello'>
<Link href='/hello'>
  <a>Next</a>
</Link>

This Link component is how we add links to pages created in a Next.js application.

If we go back to our home page and click on this link, it would take us to our /hello page.

Nuxt.js

As stated in the official documentation:

“Nuxt is a progressive framework based on Vue.js to create modern web applications. It is based on Vue.js official libraries (vue, vue-router and vuex) and powerful development tools (webpack, Babel and PostCSS). Nuxt’s goal is to make web development powerful and performant with a great developer experience in mind.”

Nuxt.js is based on Vue.js, so Vue.js developers will find it easy to get started with, and knowledge of Vue.js is required to use it.

To create a Nuxt.js app, run the following command in your CLI:

yarn create nuxt-app <project-name>
# or npx
npx create-nuxt-app <project-name>

This will prompt you to select a name, along with some other options. I named mine demo-nuxt and selected the defaults for the other options. Then, you can open your app’s folder and open pages/index.vue. Every file in this folder is turned into a route, and so our landing page will be controlled by the index.vue file. Update it with the following:

<template>
  <div class="container">
    <div>
      <logo />
      <h1 class="title">
        Hello Nuxt.js
      </h1>
      <h2 class="subtitle">
        Nuxt.js Rocks!
      </h2>
      <div class="links">
        <a
          href="https://nuxtjs.org/"
          target="_blank"
          class="button--green"
        >
          Documentation
        </a>
        <a
          href="https://github.com/nuxt/nuxt.js"
          target="_blank"
          class="button--grey"
        >
          GitHub
        </a>
      </div>
    </div>
  </div>
</template>
<script>
import Logo from '~/components/Logo.vue'
export default {
  components: {
    Logo
  }
}
</script>
<style>
.container {
  margin: 0 auto;
  min-height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  text-align: center;
}
.title {
  font-family: 'Quicksand', 'Source Sans Pro', -apple-system, BlinkMacSystemFont,
    'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  display: block;
  font-weight: 300;
  font-size: 100px;
  color: #35495e;
  letter-spacing: 1px;
}
.subtitle {
  font-weight: 300;
  font-size: 42px;
  color: #526488;
  word-spacing: 5px;
  padding-bottom: 15px;
}
.links {
  padding-top: 15px;
}
</style>

Now, run the application:

cd demo-nuxt
# start your applicatio
yarn dev # or npm run dev

Your application should be running on localhost:3000, and you should see this:

Default Nuxt.js landing page
Nuxt.js landing page (Large preview)

We can see that the page displays the content that we added to index.vue. The router structure works the same way that Next.js’ router works: It renders every file in the /pages folder into a page. So, let’s add a new page (hello.vue) to our application:

<template>
  <div>
    <h1>Hello world!</h1>
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Id ipsa vitae tempora perferendis, voluptate a accusantium itaque vel ex, provident autem quod rem saepe ullam hic explicabo voluptas, libero distinctio?</p>
  </div>
</template>
<script>
export default {};
</script>
<style>
</style>

If you open localhost:3000/hello, you should see the new page in the browser.

A Nuxt.js web page containing “Hello world”
“Hello world” page in Nuxt.js (Large preview)

Taking a Closer Look at the Differences

Now that we have looked at both static-site generators and server-side rendering, and we understand how to get started with them by using some popular tools, let’s look at the differences between them.

Static-Site GenerationServer-Side Rendering
Can easily be deployed to a static CDNCannot be deployed to a static CDN
Content and pages are generated at build timeContent and pages are generated per request
Content can get stale quicklyContent is always up to date
Fewer API calls, because it only makes them at build timeMakes API calls each time a new page is visited

Conclusion

We can see why it is so easy to think that static-generated sites and server-side-rendered applications are the same. And we know the differences between them. Try to learn more about how to build both in order to fully understand the differences between them.

Further Resources

Here are some useful links to help you get started in no time.

Smashing Editorial (ks, ra, il, al)