You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
+++
slug = "static-server-rendering-nextjs"
reading_time = "~10min"
summary = "Next.js can statically render all your pages at build time which means no processing at request time. This also allows you host your site in any site that allows static only pages. Learn how we did this for Maffin Blog"
+++
In our current version of MaffinBlog we have a function getPosts.tsx that retrieves all Github issues that we want to display as blog posts in our Blog. However, the function currently executes at request time and the first time that gets called is quite costly due to network latency and markdown parsing that happens in the background. We want to exploit generateStaticParams from Next.js so all our pages are generated before-hand!
In this post we will
⚡ Make your Blog lightning fast by pre-rendering everything at build time.
🏷️ Generate tag pages for better discoverability
➕ Bonus point: Since our blog is now fully static, we will be deploying it in Github pages!
⚡ Static rendering of our posts
Currently we retrieve the post in our PostDetailPage component like this:
which currently renders at request time. To prove that, in the terminal you can run yarn build and see that Next.js didn't render your blog page (you would see /posts/<your-slug> if it would).
Route (app) Size First Load JS
┌ ○ / 175 B 81.7 kB
└ λ /posts/[slug] 430 B 86.9 kB
[...]
○ (Static) automatically rendered as static HTML (uses no initial props)
● (SSG) automatically generated as static HTML + JSON (uses getStaticProps)
This is because Next.js has no information at build time of which blog post slugs exist. In order to give this information at build time, you need to use generateStaticParams function which generate as many pages as different slugs exist.
Note that static rendering at build time has the caveat of needing a re-deploy when you create a new issue or update one. We are okay with this as we want the speed that comes with this and we can workaround this using github hooks
This is our generateStaticParams for our blog details page:
And now, if you run yarn build you'll see that the page generated as expected:
Route (app) Size First Load JS
┌ ○ / 175 B 81.7 kB
└ ● /posts/[slug] 430 B 86.9 kB
└ /posts/github-issues-as-nextjs-blog-cms
[...]
○ (Static) automatically rendered as static HTML (uses no initial props)
● (SSG) automatically generated as static HTML + JSON (uses getStaticProps)
Even after doing this, dev mode renders your page at request time. To make sure it works you should be checking yarn build output.
It's important to know that generateStaticParams as its name says it only generates static params. In your component you still have to write code to retrieve your object and parse it as expected. This helps to keep compatibility between different approaches of rendering your components.
🏷️ Tag pages
Using the approach above we can also do the same for our tags pages. Let's say we want to have something like /tags/[tag] and then render a the list of blog posts that for that specific tag. Let's start by creating a file in src/app/tags/[tag]/page.tsx with the contents:
I want Maffin Blog infrastructure to be as simple as possible and that means no servers. Thus it means the blog has to be fully static which is what we've been working on in the previous pages. Doing this allows us to host our site in any hosting that is able to serve static content and our choice is... well Github Pages!
In order to support that, you need to add the following to next.config.js:
images: {unoptimized: true,// image optimization is not supported with export},output: 'export',// tells the compiler to generate the `out` folder with the static content
By doing this, we loose some built in functionality from Next.js. The docs about static export are really good explaining the tradeoffs.
This is all we need really, now to test it you can run again yarn build and at the end you should see an out folder at the root of your project that contains all the static content.
Next step is to configure Github so it hosts your blog. The first step is to add a new workflow configuration so it builds and deploys it (extracted from github pages examples for Next.js):
Let's start with adding a new workflow configuration that looks like this (extracted from github pages examples for Next.js):
# Sample workflow for building and deploying a Next.js site to GitHub Pages## To get started with Next.js see: https://nextjs.org/docs/getting-started#name: Deploy Maffin Blog site to Pageson:# Runs on pushes targeting the default branchpush:branches: ["static_rendering"]# Allows you to run this workflow manually from the Actions tabworkflow_dispatch:# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pagespermissions:contents: readpages: writeid-token: write# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.concurrency:group: "pages"cancel-in-progress: falsejobs:# Build jobbuild:runs-on: ubuntu-lateststrategy:matrix:node-version: [16.17.0]steps:- name: Checkoutuses: actions/checkout@v3- name: Setup Nodeuses: actions/setup-node@v3with:node-version: ${{ matrix.node-version }}cache: 'yarn'- name: Setup Pagesuses: actions/configure-pages@v3with:# Automatically inject basePath in your Next.js configuration file and disable# server side image optimization (https://nextjs.org/docs/api-reference/next/image#unoptimized).## You may remove this line if you want to manage the configuration yourself.static_site_generator: next- name: Restore cacheuses: actions/cache@v3with:path: |.next/cache# Generate a new cache whenever packages or source files change.key: ${{ runner.os }}-nextjs-${{ hashFiles('**/yarn.lock') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }}# If source files changed but packages didn't, rebuild from a prior cache.restore-keys: |${{ runner.os }}-nextjs-${{ hashFiles('**/yarn.lock') }}-- name: Install dependenciesrun: yarn- name: Build with Next.jsrun: yarn build# Only needed if you are using a custom domain- name: Set CNAME file for proper static asset loadingrun: echo '<your custom domain>' > ./out/CNAME- name: Upload artifactuses: actions/upload-pages-artifact@v1with:path: ./out# Deployment jobdeploy:environment:name: github-pagesurl: ${{ steps.deployment.outputs.page_url }}runs-on: ubuntu-latestneeds: buildsteps:- name: Deploy to GitHub Pagesid: deploymentuses: actions/deploy-pages@v2
Note that we are deploying the static_rendering branch. You can update if needed and when you've proven it works, make sure to set master. Before pushing though, you'll need to navigate to settings -> environments in your github repo and make sure you add a new rule for your branch (deployment branches).
After this, you can push your branch and you'll see the workflow in action:
Once succeeded visit the URL and you should see your blog!
💚 Conclusion
This post demonstrates how versatile Nextjs is by allowing us to just generate a bunch of static files to be hosted wherever. Not only this but we can still have dynamic behavior client side if needed. Also, Github is awesome and makes it very easy to deploy with workflows as you can see!
Feel free to navigate the code freely in here. It refers to the commit I used to ship the changes described here and you can find unit tests for the functionality added too and it adds some extra code for formatting the tag pages with Tailwind plus some refactors to Post list pages.
That's it! If you enjoyed the post feel free to react to it in the Github tracker (for now).
The text was updated successfully, but these errors were encountered:
+++
slug = "static-server-rendering-nextjs"
reading_time = "~10min"
summary = "Next.js can statically render all your pages at build time which means no processing at request time. This also allows you host your site in any site that allows static only pages. Learn how we did this for Maffin Blog"
+++
In our current version of MaffinBlog we have a function
getPosts.tsx
that retrieves all Github issues that we want to display as blog posts in our Blog. However, the function currently executes at request time and the first time that gets called is quite costly due to network latency and markdown parsing that happens in the background. We want to exploitgenerateStaticParams
from Next.js so all our pages are generated before-hand!In this post we will
⚡ Static rendering of our posts
Currently we retrieve the post in our
PostDetailPage
component like this:which currently renders at request time. To prove that, in the terminal you can run
yarn build
and see that Next.js didn't render your blog page (you would see/posts/<your-slug>
if it would).This is because Next.js has no information at build time of which blog post slugs exist. In order to give this information at build time, you need to use
generateStaticParams
function which generate as many pages as differentslugs
exist.This is our
generateStaticParams
for our blog details page:And now, if you run
yarn build
you'll see that the page generated as expected:It's important to know that
generateStaticParams
as its name says it only generates static params. In your component you still have to write code to retrieve your object and parse it as expected. This helps to keep compatibility between different approaches of rendering your components.🏷️ Tag pages
Using the approach above we can also do the same for our tags pages. Let's say we want to have something like
/tags/[tag]
and then render a the list of blog posts that for that specific tag. Let's start by creating a file insrc/app/tags/[tag]/page.tsx
with the contents:And the new
getPostsByTag
function:Running
yarn build
now will show you that it detects the new tag pages as static ones:➕ Deploying our static page to github pages
I want Maffin Blog infrastructure to be as simple as possible and that means no servers. Thus it means the blog has to be fully static which is what we've been working on in the previous pages. Doing this allows us to host our site in any hosting that is able to serve static content and our choice is... well Github Pages!
In order to support that, you need to add the following to
next.config.js
:By doing this, we loose some built in functionality from Next.js. The docs about static export are really good explaining the tradeoffs.
This is all we need really, now to test it you can run again
yarn build
and at the end you should see anout
folder at the root of your project that contains all the static content.Next step is to configure Github so it hosts your blog. The first step is to add a new workflow configuration so it builds and deploys it (extracted from github pages examples for Next.js):
Let's start with adding a new workflow configuration that looks like this (extracted from github pages examples for Next.js):
Note that we are deploying the
static_rendering
branch. You can update if needed and when you've proven it works, make sure to setmaster
. Before pushing though, you'll need to navigate tosettings -> environments
in your github repo and make sure you add a new rule for your branch (deployment branches).After this, you can push your branch and you'll see the workflow in action:
Once succeeded visit the URL and you should see your blog!
💚 Conclusion
This post demonstrates how versatile Nextjs is by allowing us to just generate a bunch of static files to be hosted wherever. Not only this but we can still have dynamic behavior client side if needed. Also, Github is awesome and makes it very easy to deploy with workflows as you can see!
Feel free to navigate the code freely in here. It refers to the commit I used to ship the changes described here and you can find unit tests for the functionality added too and it adds some extra code for formatting the tag pages with Tailwind plus some refactors to Post list pages.
That's it! If you enjoyed the post feel free to react to it in the Github tracker (for now).
The text was updated successfully, but these errors were encountered: