Download the template for this blog from Github

Next.js / Ghost Tutorial: 9 - Publish your blog

Hi there!

Over the course of the last episodes, we've build a fully functional blog site from the ground up using Ghost CMS on the backend and a Next.js at the frontend.

By now, we can only see the results by running the development server with "npm run dev" which is pretty convenient during the iterative process of creating and modifying code in our project but not suitable for production use.
The time will come for you to make your blog available to a broader audience, whether on a company intranet or the internet.

This episode guides you through the process of publishing your blog for production use.

build

If you take a look inside your project folder structure, you will find a subfolder named /.next (on UNIX derivates, the dot means it is a hidden folder). This is where the generated files live and has been there since the first time you ran "npm run dev".

Looking closer to the contents of /.next/pages you will find some *.js files well known to us, like _app.js and index.js. Besides that, _document.js and _error.js also reveal themselves, although we haven't explicitly created them. These files are managed by next.js and inherit default attributes to all regular and pages in the project, respectively.

Tip: You can place your own templates in the /pages folder of your project and modify them according to your needs. More on this under "Further reading" at the bottom of this page.

It wouldn't however be enough to take the contents of this folder and publish them to a web server since this still requires a server process to run (either "npm run dev" or "npm run start").

We therefore need to generate the static output first!

This is where "npm run build" comes into play.
On the command shell, from the top folder of your project, run:

npm run build

Depending on the size of your blog and the performance of your dev machine, this procedure might take some time. After it completes, you will have an output similar to:

...
info  - Creating an optimized production build
...
Generate static pages
...
(Static)  automatically rendered as static HTML (uses no initial props)
...

Et voilá, Next.js was busy generating the static files.
Check the /.next again and you will find all blog posts and pages ready to be published.

export

The final step before you can publish your static site is to strip out all dynamic scripts and helpers from the output and to export all the generated client side html files, images, etc.

The time for "npm run export" has come!
Again from the command shell, run:

npm run export

If all goes well, you should see something like this:

info  - using build directory: /home/filipe/code/nextjs/nextjs-ghost-production/.next
info  - Copying "static build" directory
info  - No "exportPathMap" found in "next.config.js". Generating map from "./pages"
info  - Launching 1 workers
info  - Copying "public" directory
Export successful

Inside your project you will now find the new folder /out with all files, ready to see the light of day. No more server dependencies!


Tip: Instead of running "npm run build" and "npm run export" in two steps, you may also combine both with "npm run build && npm run export" or by modifying the "build" script in your package.json to: "next build && next export".

deploy

To serve the web pages you've just exported in the previous step, you will need to host them on a web server.
If you run your own, you will probably know how to do this.

In case you don't operate your own web server and aren't planning to do so either, you will need to look for one hosting service provider out there.
Since we are looking to host a static site "only", you will find a whole lot of companies providing low cost or even free plans that are perfectly suitable!

Next.js projects natural habitat is Vercel, the company behind Next.js, so you probably want to start there first.
Besides Vercel, almost all major hosting providers like for example Netlify (BTW where this blog runs), DigitalOcean, only to name a few, are a good fit.
You may also consider go with GitHub Pages!

upload

Although some providers still provide traditional file transfer methods like FTP or Secure Copy (SCP), all of the above mentioned and many others nowadays shine through almost seamless integration into your Continuous Integration (CI) workflow by hooking themselves to your code repository (GitHub, in my case) and automatically build the pages each time you commit/push new changes.

Tip: Editing or creating a new article in Ghost doesn't trigger any changes on your Next.js project thus you wouldn't have anything to commit. I work around it by creating a new entry in the README.md file with a comment like "DDMMYY - New blog post xyz...

Sit back and enjoy ☕


Tip: Use your dev server (npm run dev) to preview your modifications before pushing them to your production blog server.


Image handling

It was only after deploying this blog to Netlify that I found that all images embedded in the blog posts were still linked to their original storage place on the development server. Of course that was not what I had planned.

After failing to find a setting in Ghost CMS to embed images using relative paths, so that they would still work once the site has been uploaded to a different location, I decided to rely on a CDN to serve all embedded media (which is recommendable for performance reasons anyway).

I'm using Amazon Web Services (AWS) with the Ghost storage adapter for S3 (link in the section "Further reading").
It was a bit tricky to get it to work (Ghost kept spitting out the error message "Unable to find storage adapter s3 in [path...]"), but after some hacking everything ended up working fine. If you are interested in more details, let me know.

Some thoughts on image optimisation:
Ghost comes with an integrated instant image optimisation that can significantly reduce load times and make your site more responsive. For every image Ghost creates a resized copy of the original. This leads to duplicated on your server / CDN.

If you don't want that and know what you are doing, you can turn this off by adding the following lines in your Ghost configuration file:

"imageOptimization": {
    "resize": false
  },

Since the recently launched Version 10, Next.js also comes with a built-in image component and automatic image optimisation.
Although I haven't covered it in this tutorial yet, this is something I might consider to explore sometime in the future.


Now that you have published your blog, you want as many people as possible to find it. Search engine optimization (SEO) plays a big role here and will therefore be the topic of the next blog post.

Stay tuned!

Filipe Matos

Further reading