How to Build a Content Publishing Calendar for a CMS
By SimpleCalendarJS Team
Every CMS eventually needs a content publishing calendar. Whether you're running a WordPress multisite, a headless CMS like Strapi or Sanity, or a custom-built admin panel — the moment your team publishes more than a few posts per week, spreadsheets and status columns stop scaling. You need a visual calendar that shows what's going out, when, and what's still in draft.
The SaaS vs. custom calendar decision
The first instinct is to reach for a SaaS editorial calendar tool. CoSchedule starts at $29/user/month ($279/month for the full Marketing Calendar). Narrato and StoryChief offer similar features at comparable price points. These tools work well for marketing teams who need built-in social publishing, approval workflows, and multi-channel distribution.
But for developers building or maintaining a CMS, SaaS editorial calendars create problems:
| Concern | SaaS tool | Custom calendar |
|---|---|---|
| Data ownership | Content metadata lives in a third party | Everything stays in your CMS database |
| Integration depth | Webhooks and API sync (often fragile) | Direct database or API queries |
| Customisation | Limited to what the vendor exposes | Full control over UI and behaviour |
| Cost at scale | $29–$279/user/month, grows with team | One-time library cost or free |
| Framework fit | Standalone app, separate login | Embedded in your existing admin UI |
If your CMS already stores posts with a status field (draft, scheduled, published) and a publishedAt timestamp, you already have everything a calendar needs. The missing piece is the visual layer — a calendar component that renders your content pipeline on a timeline.
Choosing a calendar library for a CMS
Most JavaScript calendar libraries were built for event scheduling, not content planning. But the data model is identical: a title, a start date, an optional end date, and a colour. Here's how the options compare for CMS embedding:
| Library | Bundle Size (gzip) | License | Drag & Drop | CMS Fit |
|---|---|---|---|---|
| FullCalendar | ~500 KB (full) | Free core / paid premium | Yes (premium) | Heavy for an admin panel widget |
| DayPilot Pro | ~100 KB+ | Per-developer ($849+) | Yes | Good features, commercial license required |
| DayPilot Lite | ~50 KB | Apache 2.0 | Limited | Free but missing key features |
| React Big Calendar | ~50 KB (+ React) | MIT | Community plugin | React-only, no vanilla JS |
| CalendarJS | ~40 KB | MIT | Yes | Limited ecosystem |
| SimpleCalendarJS | ~18 KB | Free personal / license req. commercial | Yes | Lightweight, framework-agnostic |
For a CMS admin panel, the priority is small footprint and fast load. Admin pages already carry their own framework, rich text editors, media uploaders, and navigation. Adding 500 KB for FullCalendar — or paying $849+ per developer for DayPilot Pro — doesn't make sense when the calendar's job is to show posts on a timeline and let editors drag them to reschedule.
Building the publishing calendar
Step 1: Define the content-to-event mapping
Your CMS stores posts. The calendar displays events. The mapping is straightforward:
function postsToCalendarEvents(posts) { const statusColors = { draft: '#f59e0b', scheduled: '#3b82f6', published: '#10b981', review: '#8b5cf6', }; return posts.map((post) => ({ id: post.id, title: post.title, start: new Date(post.publishedAt || post.createdAt), end: new Date(post.publishedAt || post.createdAt), color: statusColors[post.status] || '#6b7280', })); }
Draft posts show in amber, scheduled posts in blue, published posts in green, and posts under review in purple. Editors can scan the calendar and immediately see the pipeline's health — too many amber dots means the review queue is backed up.
Step 2: Mount the calendar in your admin panel
npm install simple-calendar-js
import SimpleCalendarJs from 'simple-calendar-js'; import 'simple-calendar-js/dist/simple-calendar-js.min.css'; const calendar = new SimpleCalendarJs('#editorial-calendar', { defaultView: 'month', locale: 'en-US', enabledViews: ['month', 'week'], fetchEvents: async (start, end) => { const res = await fetch( `/api/cms/posts?from=${start.toISOString()}&to=${end.toISOString()}` ); const posts = await res.json(); return postsToCalendarEvents(posts); }, onEventClick: (event) => { window.location.href = `/admin/posts/${event.id}/edit`; }, onSlotClick: (date) => { window.location.href = `/admin/posts/new?publishAt=${date.toISOString()}`; }, });
Clicking a post on the calendar opens the editor. Clicking an empty date slot creates a new draft pre-filled with that publish date. The fetchEvents callback runs on every navigation, so the calendar always reflects the latest content pipeline state.
Step 3: Add the API endpoint
Your CMS needs an endpoint that returns posts within a date range. Here's a minimal example for a Node.js backend with a SQL database:
app.get('/api/cms/posts', async (req, res) => { const { from, to } = req.query; const posts = await db.query( `SELECT id, title, status, published_at, created_at FROM posts WHERE (published_at BETWEEN $1 AND $2) OR (published_at IS NULL AND created_at BETWEEN $1 AND $2) ORDER BY COALESCE(published_at, created_at)`, [from, to] ); res.json( posts.rows.map((row) => ({ id: row.id, title: row.title, status: row.status, publishedAt: row.published_at, createdAt: row.created_at, })) ); });
For headless CMS platforms like Strapi or Sanity, replace the SQL query with the platform's content API. Strapi uses REST or GraphQL; Sanity uses GROQ. The calendar doesn't care where the data comes from — it only needs an array of events with title, start, and color.
Step 4: Integrate with WordPress
WordPress has its own post scheduling system, but the built-in calendar view is limited to a list in the Posts screen. To add a visual editorial calendar to the WordPress admin:
function editorial_calendar_admin_page() { add_menu_page( 'Editorial Calendar', 'Calendar', 'edit_posts', 'editorial-calendar', 'render_editorial_calendar', 'dashicons-calendar-alt', 5 ); } add_action('admin_menu', 'editorial_calendar_admin_page'); function render_editorial_calendar() { wp_enqueue_script( 'simple-calendar-js', plugins_url('dist/simple-calendar-js.min.js', __FILE__), [], '1.0.0', true ); wp_enqueue_style( 'simple-calendar-css', plugins_url('dist/simple-calendar-js.min.css', __FILE__) ); echo '<div id="editorial-calendar" style="max-width:960px;margin:20px auto;"></div>'; }
Then initialise the calendar with the WordPress REST API:
const calendar = new SimpleCalendarJs('#editorial-calendar', { defaultView: 'month', fetchEvents: async (start, end) => { const res = await fetch( `/wp-json/wp/v2/posts?after=${start.toISOString()}&before=${end.toISOString()}&status=draft,pending,future,publish&per_page=100`, { headers: { 'X-WP-Nonce': wpApiSettings.nonce } } ); const posts = await res.json(); return posts.map((p) => ({ id: p.id, title: p.title.rendered, start: new Date(p.date), color: p.status === 'publish' ? '#10b981' : p.status === 'future' ? '#3b82f6' : '#f59e0b', })); }, onEventClick: (event) => { window.location.href = `/wp-admin/post.php?post=${event.id}&action=edit`; }, });
This gives WordPress editors a month-view calendar showing all posts — drafts, scheduled, and published — without installing a third-party plugin that adds its own database tables, telemetry, or upsells.
Step 5: Theme the calendar for your admin panel
CMS admin panels have their own visual language. The calendar should match it, not clash. SimpleCalendarJS uses CSS custom properties:
#editorial-calendar .uc-calendar { --uc-primary: #2563eb; --uc-bg: #ffffff; --uc-border: #e2e8f0; --uc-text: #1e293b; --uc-radius: 6px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; } @media (prefers-color-scheme: dark) { #editorial-calendar .uc-calendar { --uc-bg: #1a1a2e; --uc-border: #2d2d44; --uc-text: #e2e8f0; } }
For WordPress, match the admin colour scheme. For Strapi or a custom React admin, pull values from your existing design tokens.
When SaaS editorial tools make more sense
Building a custom content calendar makes sense when your team is developer-led and your CMS already manages the content pipeline. But SaaS tools earn their cost in specific situations:
- Non-technical marketing teams who need drag-and-drop social scheduling, approval workflows, and direct publishing to Twitter, LinkedIn, and Facebook — CoSchedule ($29+/user/month) or StoryChief handles this without code
- Multi-channel content operations where blog posts, emails, social posts, and ads all need to appear on one timeline — purpose-built tools like Narrato or DivvyHQ coordinate across channels
- Enterprise publishers with dozens of editors, compliance requirements, and audit trails — platforms like Kordiam (formerly Desk-Net) are built for newsrooms
If your need is simpler — see what's publishing this week, reschedule a draft by clicking a date, colour-code posts by status — a ~18 KB calendar library mounted in your existing admin panel does the job at a fraction of the cost and complexity.
Summary
- SaaS editorial calendar tools like CoSchedule ($29+/user/month) and StoryChief work for marketing teams but add cost, vendor dependency, and integration friction for developer-led CMS setups
- Your CMS already stores the data a calendar needs — post title, status, and publish date — the missing piece is the visual layer
- FullCalendar (~500 KB) and DayPilot Pro ($849+/dev) are overkill for an admin panel widget that shows content on a timeline
- SimpleCalendarJS adds a full month/week calendar at ~18 KB gzipped — map post statuses to colours, wire up
fetchEventsto your CMS API, and editors get a scannable content pipeline - Works with WordPress (via the REST API), headless CMS platforms like Strapi and Sanity (via their content APIs), and any custom admin panel
Sources & Further Reading
Research & References
- 13 Editorial Calendar Software for Efficient Content Planning — State of Digital Publishing
- Best 15 Top Editorial Content Calendar Software Tools (2026) — SoftwareTestingHelp
- I Tested 12 of the Best Editorial Calendar Tools in 2026 — Narrareach
- Best Editorial Content Planning Tools of 2026 — Kordiam
- CoSchedule Pricing Review: 2026 Calendar Plans & Costs — SocialChamp
- Open-Source JavaScript Scheduler and Calendar Components — DayPilot
- FullCalendar — JavaScript Event Calendar
- Scheduled Content Publishing: Step-by-Step Guide 2026 — TrySight
- 12 Best Content Calendar Software, Compared and Ranked — Asana
Image Credits
- Cover: A Person Writing on Calendar — Pexels
All images free to use under the Pexels License.
Frequently Asked Questions
What is a content publishing calendar?
A content publishing calendar (also called an editorial calendar) is a visual schedule that shows what content will be published, when, and on which channel. It helps content teams plan blog posts, social updates, newsletters, and other content in a single timeline view.
Should I build a custom content calendar or use a SaaS tool like CoSchedule?
If your CMS already has a content pipeline and you want full control over the UI and data, building a custom calendar with a JavaScript library is cheaper and more flexible. SaaS tools like CoSchedule ($29+/user/month) make sense for non-technical marketing teams who need built-in social publishing and approval workflows.
Can I use SimpleCalendarJS inside a CMS admin panel?
Yes. SimpleCalendarJS is framework-agnostic and mounts to any DOM element. It works inside WordPress admin pages, headless CMS dashboards built with React or Vue, and custom admin panels — anywhere you can render HTML and load JavaScript.
How do I display draft and scheduled posts on a calendar?
Query your CMS for posts with status 'draft', 'scheduled', or 'published', then map each post to a calendar event with a title, date, and colour based on its status. Pass these events to the calendar's fetchEvents callback so the view updates as dates change.
What JavaScript calendar library is best for a CMS editorial calendar?
It depends on your requirements. FullCalendar (~500 KB full bundle) has the most features but adds significant weight. DayPilot Pro requires per-developer licensing. SimpleCalendarJS (~18 KB gzipped) is the lightest option with month, week, and day views — ideal for embedding in a CMS admin panel without bloating the backend.
Add a calendar to your app today
Free for personal projects. $49/year or $199 lifetime per commercial project.
