Jekyll Drafts and Publishing Workflow: How to Manage Content
How to use Jekyll drafts, future posts, unpublished pages, and a full editorial workflow — from idea to published post.
Jekyll gives you several ways to keep content out of your public site while you work on it: the _drafts/ folder, published: false front matter, and future-dated posts. Understanding these tools lets you build a proper editorial workflow without a CMS.
The _drafts folder
The simplest way to keep work-in-progress posts off your live site. Create a _drafts/ directory at your project root and put unfinished posts there:
_drafts/
├── jekyll-drafts-publishing-workflow.md
├── ideas-for-q3.md
└── half-finished-tutorial.md
Unlike _posts/, draft filenames do not need a date prefix:
# _posts/ — date required
_posts/2026-08-09-my-post.md
# _drafts/ — no date
_drafts/my-post.md
Previewing drafts locally
Normal jekyll serve ignores _drafts/ entirely. To preview your drafts:
bundle exec jekyll serve --drafts
When --drafts is active, Jekyll assigns today’s date to draft posts and includes them in the site. You can see exactly how a draft will look when published.
Drafts in CI/production
Your CI/CD build command should never include --drafts:
# Correct — drafts excluded
JEKYLL_ENV=production bundle exec jekyll build
# Wrong — would publish drafts
JEKYLL_ENV=production bundle exec jekyll build --drafts
Drafts are never published accidentally as long as you do not pass --drafts to your production build.
published: false
An alternative to _drafts/ — set published: false in any post’s front matter to exclude it from the build:
---
layout: post
title: "Work in Progress"
date: 2026-08-09
published: false
---
Content here will not appear on the live site.
This works for posts in _posts/ and pages anywhere on your site. Useful when:
- You want to keep the file in
_posts/(with a date) but not publish it yet - You want to temporarily hide a published post without deleting it
- You want to keep old content for reference but remove it from the site
Preview unpublished content locally:
bundle exec jekyll serve --unpublished
Future-dated posts
Jekyll excludes posts whose date is in the future by default. Write a post today, set a future date, and it will automatically appear on your site on that date — the next time your site builds.
---
layout: post
title: "My Scheduled Post"
date: 2026-09-01
---
This post will not appear in site.posts until September 1, 2026 (or whenever you trigger a build after that date).
Preview future posts locally:
bundle exec jekyll serve --future
Scheduling posts with CI/CD
Future posts are only published when your site rebuilds after their date. If you do not rebuild frequently, a future post will sit in your repository unpublished.
Set up a scheduled CI/CD build to rebuild daily. On Netlify:
# Netlify → Site settings → Build hooks
# Add a build hook and schedule it with a cron service (cron-job.org)
# to trigger the hook daily at midnight
On GitHub Actions:
# .github/workflows/scheduled-build.yml
name: Scheduled Build
on:
schedule:
- cron: "0 0 * * *" # midnight UTC every day
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: "3.2"
bundler-cache: true
- run: JEKYLL_ENV=production bundle exec jekyll build
# add your deploy step here
Combining methods
The three approaches can be combined:
| Situation | Method |
|---|---|
| Early draft, no date yet | _drafts/ folder |
| Finished but not ready | published: false in _posts/ |
| Ready but timed for later | Future date in _posts/ |
| Temporarily hidden live post | published: false |
A practical editorial workflow for Jekyll
Here is a workflow that works well for solo writers and small teams:
Stage 1: Idea capture
Create a file in _drafts/ with just a title and outline:
<!-- _drafts/jekyll-pagination-guide.md -->
---
title: "Jekyll Pagination: The Complete Guide"
tags: [jekyll, pagination, tutorial]
---
## Outline
- What is pagination
- jekyll-paginate vs jekyll-paginate-v2
- Setup
- Customising the paginator
- SEO considerations
Stage 2: Writing
Fill in the content in _drafts/. Preview with jekyll serve --drafts at any point.
Stage 3: Review and polish
Run a local preview and check:
- All code examples work
- Images are in place
- Internal links resolve
- SEO: title, description, image set in front matter
Stage 4: Schedule or publish
Publish immediately: Move the file from _drafts/ to _posts/ and add today’s date:
mv _drafts/jekyll-pagination-guide.md _posts/2026-08-09-jekyll-pagination-guide.md
Schedule for later: Move to _posts/ with a future date:
mv _drafts/jekyll-pagination-guide.md _posts/2026-09-15-jekyll-pagination-guide.md
Keep hidden while finalising: Move to _posts/ with today’s date but published: false:
---
published: false
date: 2026-08-09
---
Set published: true (or remove the line) when ready.
Stage 5: Push and deploy
Commit and push. Your CI/CD pipeline builds and deploys the updated site.
Working with collaborators
For teams using GitHub:
Use pull requests for drafts. Writers work on a branch. Open a PR when the post is ready for review. Reviewers see a preview deployment (Netlify/Cloudflare/Vercel create these automatically). Merge when approved.
Use _drafts/ for long-running work. Content that spans multiple sessions lives safely in _drafts/ in its own branch.
Use published: false for quick feedback. Push to main with published: false to get a staging URL the reviewer can share without it going live.
Adding a headless CMS for non-technical editors
If your team includes non-developers who are not comfortable with Git and Markdown, add Decap CMS (formerly Netlify CMS) for a visual editing interface:
# admin/config.yml
collections:
- name: drafts
label: Drafts
folder: _drafts
create: true
slug: "{{slug}}"
fields:
- { label: Title, name: title, widget: string }
- { label: Body, name: body, widget: markdown }
- name: posts
label: Posts
folder: _posts
create: true
slug: "{{year}}-{{month}}-{{day}}-{{slug}}"
fields:
- { label: Title, name: title, widget: string }
- { label: Publish Date, name: date, widget: datetime }
- { label: Published, name: published, widget: boolean, default: false }
- { label: Body, name: body, widget: markdown }
Non-developers can write in a rich text editor and save as drafts. The published: false toggle keeps posts off the live site until ready.
Useful command reference
# Normal serve (no drafts, no future posts)
bundle exec jekyll serve
# Show all drafts
bundle exec jekyll serve --drafts
# Show future-dated posts
bundle exec jekyll serve --future
# Show unpublished posts (published: false)
bundle exec jekyll serve --unpublished
# Show everything
bundle exec jekyll serve --drafts --future --unpublished
# Production build (nothing extra)
JEKYLL_ENV=production bundle exec jekyll build
Tips for a smooth workflow
Name draft files descriptively. Even without dates, jekyll-pagination-complete-guide.md is much easier to find than draft-post-3.md.
Keep drafts short at first. Write the outline and key points, then expand. A complete outline in _drafts/ is better than a blank file in _posts/.
Commit drafts to Git. _drafts/ should be in your repository, not gitignored. This gives you version history, backup, and branch-based collaboration.
Set a date in front matter while drafting. Even if you are not ready to publish, set an estimated date in front matter so you can preview how it will sort among your posts.
Jekyll’s draft and publishing system is simple but complete. Once you have a consistent workflow, writing and scheduling content becomes as smooth as any CMS — with the added benefit of full version control.
Collaborative workflows with Jekyll drafts
For blogs with more than one author or editor, the drafts workflow needs structure beyond simply keeping files in _drafts/. Git branches provide the structure: each article in progress lives on its own branch, and the merge to main is the publishing action. This approach has several advantages over shared _drafts/ folders — authors work independently without seeing each other’s work-in-progress, editors can review by checking out the branch or viewing a pull request diff, and the history of each article is cleanly isolated.
Configure GitHub Actions to deploy preview branches automatically. With Netlify or Cloudflare Pages connected to your repository, every push to any branch creates a preview URL. An author writing on a branch named post/jekyll-seo-tips gets a preview at post-jekyll-seo-tips.yoursite.pages.dev. They can share this link for editorial review without requiring the reviewer to run Jekyll locally. The merge to main triggers a production deployment. This workflow is robust, version-controlled, and requires no CMS software.
For teams where authors are not comfortable with Git branching, Decap CMS layered on top of Jekyll provides a graphical interface for the same workflow. Decap CMS creates a GitHub PR for each draft when the editorial workflow is enabled — non-technical authors write in a rich text editor, and the underlying mechanism is the same branch-per-draft Git model. The technical implementation is identical; only the interface changes.
Scheduling posts in Jekyll
Jekyll does not have native post scheduling — it is a build tool, not a runtime server. Posts with future dates are excluded from the default build, which means scheduling requires triggering a build at the right time.
The cleanest scheduling approach is a GitHub Actions workflow with a cron trigger. Set up a workflow file that runs bundle exec jekyll build on a schedule — daily at midnight, for instance. Posts with dates up to and including that day are included in the build and thus appear on the site. Authors set the desired publication date in the post’s front matter and commit the file to the main branch; the scheduled build picks it up when the date arrives.
A sample GitHub Actions schedule trigger looks like:
on:
schedule:
- cron: '0 0 * * *' # runs at midnight UTC daily
push:
branches: [main]
This runs the build on every push (for immediate publishing) and once daily at midnight (to publish scheduled posts). The combination ensures immediate deployment for posts dated today or earlier, and automatic publication for future-dated posts as their dates arrive.
Cloudflare Pages and Netlify both support build hooks — URLs you can POST to trigger a rebuild. Integrating a build hook with a CRON service (like EasyCron or GitHub Actions) gives you the same scheduled publishing capability on any host.
Handling editorial review before publishing
For high-stakes content — sponsored posts, legal content, press releases, guest contributions — a formal editorial review step before publishing adds quality assurance that informal drafts workflows lack. The Git pull request model provides this naturally: the author submits a PR from their draft branch, the editor reviews the diff, leaves comments, requests changes, and approves when satisfied. The merge to main publishes the post.
Within the PR, reviewers can leave line-level comments on specific sentences or paragraphs in the Markdown file. Authors address each comment, push revisions, and the reviewer re-reads. This iterative review process is identical to code review — and it works remarkably well for content because the diff format makes changes clear and the history is preserved permanently.
For teams uncomfortable with GitHub’s PR interface, Prose.io provides a graphical editor for Jekyll repositories that creates and edits files through the GitHub API. Authors write in a visual editor, save to a branch, and the tech-comfortable editor reviews and merges via the GitHub PR interface. The two groups never need to use the same tool; the Git repository is the shared layer.
From draft to evergreen: maintaining published content
Publishing is not the end of a post’s lifecycle. Evergreen technical content — how-to guides, comparison articles, reference posts — benefits from periodic updates to keep examples current, statistics accurate, and recommendations relevant. An update strategy prevents posts from becoming outdated and keeps rankings stable.
Add a last_updated field to post front matter when you make meaningful content changes. Configure your post layout to show both the original publication date and the last updated date when they differ — this signals to readers that the content is maintained, which increases trust and reduces bounce rates from readers who notice a date from three years ago and assume the content is stale.
Set a content review calendar: choose a frequency (quarterly for most posts, monthly for fast-moving topics) and review each published post against that schedule. The review questions are: Is the information still accurate? Have any links broken? Are there new alternatives or tools worth mentioning? Has the recommended approach changed? A thirty-minute review session per post, twice a year, keeps a blog of fifty posts in excellent condition without feeling overwhelming.
Jekyll’s Git history makes content archaeology easy — you can see exactly what changed in a post at any point in its history, which is useful for understanding why something was written a certain way or when a specific claim was added. This transparency is one of the underrated long-term benefits of a Git-based content workflow over a database-driven CMS where content history is often opaque or unavailable.