DEV Community

Ryan Feigenbaum
Ryan Feigenbaum

Posted on • Originally published at on

How to Set Up a Dev Environment for Creating a Custom Ghost Theme

How to Set Up a Dev Environment for Creating a Custom Ghost Theme

In this guide, I'll show you how I set up my dev environment for creating a custom Ghost theme. The key part of this environment is Rollup, which I use to compile, bundle, and minify my JavaScript and CSS files.

My Rollup config–in addition to some other goodies like a script to start a LiveReload server and one to zip theme files–are available as a template on GitHub. The template includes the basic Handlebars files needed for a Ghost theme, Rollup, PostCSS, basic JS and CSS, along with some explanatory comments. The template builds off of the official Ghost theme Starter.

Below, I walk through the template and show how to use it.

GitHub - royalfig/ghost-theme-starter

📢 My Ghost theme starter is a work in progress. I'll be updating and adding to it, so check back often. Contributions are also welcome!


This guide assumes you have a local version of Ghost installed. If you don't, the official documentation should help you get set up. You'll need some basic familiarity with the command line and a code editor like VS Code.

If you've never built a Ghost theme before, you'll want to read through the docs to understand how the template layer works. You can also look at my custom themes, Smart and Mel, or the default Ghost theme, Casper, to see real-world examples.

If you're brand new to building Ghost themes and would like more articles walking you through the basics–let me know.

Get the Template

Go to my ghost-theme-starter and click Use the template. (You can also just clone the repo directly.) This will create a copy of the template in your GitHub account. You can then clone the repo to your local machine.

Go to the directory where you installed the theme starter and install dependencies with npm install. Then, create a symlink from the theme starter to your Ghost's themes folder ( content/themes). The symlink allows you to work on your theme in a folder that's independent of your Ghost install, yet still links to it directly. This way you can reinstall Ghost without having to worry about your theme files or upload a production version of the theme to your local install without worrying about overwriting your local development copy.

# symlink your theme to your local Ghost install
ln -s path-to-ghost-theme-starter ghost-install/content/themes
Enter fullscreen mode Exit fullscreen mode

Create a symlink between your theme and the Ghost themes folder

Run ghost restart so Ghost recognizes your new theme. Activate your theme and you're off to the races.

Make It Pretty

Styles live in src/css, and files are organized into a 7-1 architecture. While the 7-1 architecture was originally made popular with SASS, it's used here with several plain CSS files split across seven categories with one main app.css file. I find that CSS has evolved enough with custom properties and PostCSS that SASS is now unnecessary and sometimes more of a burden than a convenience.

Folder Description Example
abstracts/ Variables used across the project variables.css
base/ Base styles for the project reset.css
layout/ Elements found on every page navbar.css
pages/ Styling for particular templates like post.hbs or home.hbs post.css
components/ Components used frequently like buttons or forms buttons.css
vendor/ Styling for 3rd party elements ghost.css
themes/ Multiple color or typography schemes Not used

Use the command npm run dev to watch for changes in src/css, compile your CSS, and provide a sourcemap, so when you inspect your styles in the dev console, you'll see the exact file and line of code responsible for breaking your design.

In production, initiated with npm run zip, stylelint will lint your CSS, automatically ordering properties according to a recess logic. CSS will be autoprefixed and polyfilled with postcss-preset-env (like a Babel for CSS). One example of this polyfill is automatically writing fallbacks for custom properties. Finally, the CSS is minified and optimized with cssnano. It's just up to you to ship those styles 🛳️.

PostCSS - a tool for transforming CSS with JavaScript

Put It to Work

Every KB counts

For JS, the story's the same. Rollup compiles your JS files in src/js. What's different here is that there are two different output bundles: app.js and post.js. App.js is loaded globally on every page, whereas post.js is only loaded on posts and pages. The thinking here is that you likely have scripts that are only needed on posts and pages (like responsive embeds) that aren't needed on index pages, so you can reduce your homepage's bundle size by excluding this code from the app's main JS file.

Babel is also active to help compatibility across browsers along with a handful of other Rollup plugins to make it easier to work with next-gen JS and third-party libraries.

Reload It Like It's Hot

Rollup has a built-in ability to watch your files and compile them when you save edits. The problem is those changes won't show up in your browser without a hard refresh.

The theme starter template uses a custom implementation of LiveReload to automatically refresh your browser whenever you make changes to your CSS or JS. This makes it easy to see your changes in real-time, without having to lift a finger.

This reloading is limited to JS and CSS changes and not to Handlebars files. If you make a change to a template file, you're stuck having to reload the browser. (It may be possible to tell LiveReload to watch for changes to .hbs files, too, but I haven't got it to work yet.) You also need to restart your Ghost instance if you add a new template file altogether.

Zip, Zip, and Away

The final piece of this puzzle is a custom script for zipping your files up into a file that is suitable to upload to Ghost. If you add new template files, you'll need to adjust zip.js to include them.

It's also possible, instead of zipping your files and uploading yourself, to just use Ghost's Github action, which will automatically deploy your theme whenever you push a change to your repo's main remote branch.

The ghost-theme-starter will set you up with a great working environment to build your next Ghost theme. Try it out and let me know what you think.

Top comments (4)

arlopezg profile image
Alejandro López

I've been running a similar setup with Gulp (I know, it came installed in the theme my client is running) and the GitHub Action - works flawlessly!

I would also recommend semver-tagging your releases any time you push to master. Makes it a breeze to rollback in case anything goes awry.

royalfig profile image
Ryan Feigenbaum

Is there a package you use for that? Or do you do it manually?

arlopezg profile image
Alejandro López

I use this action: phips28/gh-action-bump-version@v8.... and then generate a changelog with this other one fregante/release-with-changelog@v3. They play out nicely and you can easily check out what's new in every release.

There's also the standard-version package on NPM if you're interested in doing this as "locally" as possible, running on Git hooks.

Thread Thread
royalfig profile image
Ryan Feigenbaum

This is great! Thank you. Will be adding it to my workflows soon