gh-pages: publish a build directory to a branch with one npm call
General purpose task for publishing files to a gh-pages branch on GitHub
At a glance
- What is it?
- The gh-pages npm package copies a build folder into a gh-pages branch and pushes it. It suits static site output that can be regenerated, and it deletes anything in the branch that is not in your source.
- Who is it for?
- Adopt gh-pages if your site is a build output you can regenerate, you already have a git remote, and you want the branch updated from a single publish call. Do not adopt it if the branch holds files that exist nowhere else, or if you need a preview of the push before it lands.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Activity is slowing. The repository last received commits 6 months ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem gh-pages solves: getting build output onto a branch
GitHub Pages serves a site from a branch in your repository. That creates an awkward step for any project with a build step. The source lives on main, the compiled output lives nowhere, and the Pages branch has to contain the compiled files at its root, not inside a dist folder. Doing this by hand means a second working tree, a checkout of an unrelated branch, a copy, a commit and a push, repeated on every release.
The gh-pages package turns that into one function call. Its own description is "General purpose task for publishing files to a gh-pages branch on GitHub", and the README keeps the interface small: a directory, an options object, a callback. It is aimed at people who already build a static site with a tool like a bundler and want the output published without learning the plumbing of orphan branches.
It is not a hosting product. GitHub Pages does the hosting. This package only moves files into the branch that GitHub Pages reads.
What publish actually does to your repository
The README describes the sequence plainly: publish creates a temporary clone of the current repository, creates a gh-pages branch if one does not exist, copies files from the base path or the subset matching src, commits, and pushes to the origin remote. If the branch already exists, it is updated with all commits from the remote before the new files are added.
That temporary clone is the important detail. Your working tree is not checked out to another branch, so uncommitted work in the main checkout is untouched. The cost is that the package needs a real git remote to talk to. The repo option defaults to the origin remote URL of the current directory, which means the current directory must be a git repository unless you pass repo explicitly.
The default src is '**/*', a minimatch pattern, and dest defaults to '.', so files land at the root of the target branch. The README warns in bold that files in the branch which are not in src will be removed. This is a mirror, not an append. If a previous deploy wrote an asset that your current build no longer produces, that asset disappears on the next publish. The add option exists for the opposite behaviour: add new files and never remove existing ones.
Installing gh-pages and a first deploy
The README installs it as a development dependency, since it runs at publish time rather than in the browser. It requires Git >= 1.9 and Node > 14 according to the README; the package.json engines field says node >=10, so the README is the stricter and more current statement of the two.
npm install gh-pages --save-devWith a build directory named dist, the minimal script is three lines. The callback receives an error if the clone, commit or push fails.
var ghpages = require('gh-pages');
ghpages.publish('dist', function(err) {});Given a dist folder containing index.html and js/site.js, the README states that this creates a gh-pages branch whose root holds index.html and js/site.js. Nothing is wrapped in a dist directory. A typical package.json script wires it to the build.
{
"scripts": {
"deploy": "npm run build && gh-pages -d dist"
}
}The package also ships two binaries, gh-pages and gh-pages-clean, declared in package.json. The README documents the -b/--branch and -r/--repo flags for command line use. Run the command and watch the output: it prints the clone, the commit and the push, and the callback or process exit code reports failure.
Options that matter once the first deploy works
Four options decide whether the tool fits a real project.
nojekyll writes a .nojekyll file, which the README links to GitHub's post on bypassing Jekyll on GitHub Pages. Any output directory containing files or folders that start with an underscore, which is common for asset pipelines, will be dropped by Jekyll processing without it. This is the option most people need and the one most often forgotten.
cname writes a CNAME file with a custom domain. It is a convenience, and it is also a trap: if you set the custom domain in the repository settings and then publish without cname, the file is not in src and the mirror behaviour removes it.
dotfiles defaults to false, so files beginning with a dot are ignored unless they appear explicitly in the src array. A build that emits .well-known or a hidden config file will silently lose it.
user matters in CI. The README states that in a repository without user.name or user.email git config, or on a machine without global config, you must supply options.user with name and email before git will commit. Most hosted runners have no global identity, so a deploy that works locally can fail on the first CI run with a git error that has nothing to do with gh-pages itself.
Where gh-pages is the wrong tool
The mirror behaviour is the sharpest limitation. Anything in the target branch that is not matched by src is deleted. If the branch contains a hand-maintained file, a legacy asset, or a generated file from a different job in the same pipeline, that file is gone after the next publish. The add option avoids deletion, but it also means stale files accumulate forever, because nothing removes them. There is no middle ground in the documented options between mirroring and never deleting.
The second limitation is the temporary clone. Every publish clones the repository and fetches the existing branch. On a large repository history this is more work than a plain push, and it requires network access and credentials at publish time. A build machine with no git remote, or with a shallow checkout that lacks the remote URL, needs the repo option filled in.
Third, there is no dry run in the documented options. Nothing in the README describes a way to see the resulting file list before the push happens. If you need that, you are checking out the branch yourself.
Finally, the tool assumes GitHub Pages style hosting. Pushing a branch to a remote that does not serve branches as sites gains you nothing beyond a branch with files in it.
Alternatives and how they differ
The most direct alternative is GitHub's own Pages build and deployment through Actions. That approach uploads an artifact and lets the Pages service deploy it, so there is no second branch holding the site and no clone step. The difference is where the state lives: gh-pages keeps the published output as commits in a branch of your repository, which you can inspect, tag and diff with ordinary git commands. An artifact-based deploy keeps the output outside the repository. If you value being able to read the deployed site as a branch, gh-pages gives you that; if you would rather not maintain a second branch, the artifact route avoids it.
A second alternative is a manual orphan branch workflow: git checkout --orphan gh-pages, clear the index, copy the build output, commit, push. It is the same result with more commands and no npm dependency. The trade-off is that gh-pages encodes the sequence, the src filtering and the .nojekyll and CNAME file writing, which is exactly the part people get wrong by hand.
A third option is any static host that takes a directory upload directly. Those remove the branch entirely, at the cost of leaving GitHub's infrastructure. gh-pages is only interesting if the branch is the deployment target you want.
Maintenance, licence and upgrade cost
The repository is not archived, and the last push was on 2026-03-26. The most recent release listed is v6.3.0 from 2025-01-02, preceded by v6.2.0 in October 2024 and v6.1.1 in December 2023. That is a low release cadence: the interface is small and stable, and the changelog file at the repository root is the place to check what changed between versions.
The package is MIT licensed. In practice that means you can use it in commercial and closed-source projects, keep the copyright notice, and are not granted any warranty. The published npm package includes only the lib and bin directories, so the test and task folders you see in the repository are not part of what you install. Do not treat that as legal advice; read the LICENSE file in the repository if the terms matter to your organisation.
Upgrade cost is low on the surface, because the documented API is one function and a set of options. The risk sits in the dependency chain: commander, fs-extra, tinyglobby, filenamify and find-cache-dir are all runtime dependencies, so a major bump in any of them arrives with a routine npm update. The lib and test directories exist in the repository, and the package runs mocha with eslint as a pretest step, so a fork or a patch can be verified with npm test.
Editorial conclusion
Adopt gh-pages if your site is a build output you can regenerate, you already have a git remote, and you want the branch updated from a single publish call. Do not adopt it if the branch holds files that exist nowhere else, or if you need a preview of the push before it lands. Before the first run, check that the gh-pages branch has no hand-edited files, that a git user identity is available or supplied through options.user, and that your src patterns cover everything the branch should keep.
Frequently asked questions
What is the gh-pages branch?
It is the branch the gh-pages package publishes to by default, and the branch GitHub Pages reads when it serves a site. The README states that publish creates the branch if it does not already exist, then copies files into it and pushes to the origin remote.
How do I install gh-pages?
Install it as a development dependency with npm install gh-pages --save-dev. The README notes that the module requires Git >= 1.9 and Node > 14.
How do I use gh-pages npm to publish a build?
Require the module and call publish with your build directory and a callback, as in ghpages.publish('dist', function(err) {}). With a dist folder holding index.html and js/site.js, the README says the resulting branch has those files at its root.
How do I set up gh-pages for a project?
Install the package, build your site into a directory, and call publish on that directory. The README documents options for the branch name, the destination folder, the commit message and the committer identity used when git config is missing.
What is the gh-pages alternative if I do not want a second branch?
The README does not discuss alternatives. It documents pushing to a branch on a remote, and the branch option lets you target any branch on any remote, but the package always produces commits on a branch rather than deploying an artifact.
What is gh-pages used for?
It publishes files to a gh-pages branch on GitHub, or to any other branch on any other remote, according to the package description. The README frames it as a general purpose task for moving a build directory into a branch.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/tschaub-gh-pages)