Product Overview

PCOWidget is a self-contained, embeddable calendar that pulls live event data from your Planning Center Online calendar and renders it as an interactive, styled component on any web page — with a single <script> tag.

📅
Six Layout Views
Month grid, week strip, list rows, card grid, compact image tiles, and simple service tiles.
🔍
Event Popovers
Click any event for a full-viewport detail card with image, description, and action links.
🏷️
Tag Filtering
Filter by Planning Center tag — show only Youth, Worship, Outreach events on any page.
📖
Sermon Title Extraction
Write sermon: Your Title in PCO event notes — it surfaces automatically on service tiles.
🎨
Auto Tag Colors
Events auto-color by ministry category — Worship, Youth, Children, Outreach, and more.
📆
iCal Downloads
"Add to Calendar" links let visitors save events to Google Calendar, Outlook, or Apple Calendar.
📱
Mobile-First Responsive
Adapts from phone to desktop. Works in Squarespace, WordPress, Wix, and custom HTML sites.
🌎
Your Church's Local Time
Event times are shown in the timezone set on your Planning Center organization, detected automatically. Nothing to configure.
⚡
No Backend Required
PCOWidget's servers handle PCO authentication. Your site only needs a script tag.

How It Works

PCOWidget sits between your church website and Planning Center. Your site never touches PCO credentials — they live encrypted on PCOWidget's servers.

Your Church Website (Squarespace, WordPress, Wix, HTML)
    <script src="https://api.pcowidget.com/v1/calendar-widget.js?key=YOUR_KEY">
                              ↓
                    api.pcowidget.com
                              ↓
    Your API key → your PCO credentials (encrypted, server-side)
                              ↓
           api.planningcenteronline.com/calendar/v2
                              ↓
    Events rendered as HTML → injected into your page
  1. Your <script> tag loads the widget JavaScript from PCOWidget's API servers
  2. The widget auto-detects its endpoint from its own src URL
  3. PCOWidget's PHP middleware authenticates with Planning Center using your encrypted credentials (never exposed to visitors)
  4. Events are fetched, formatted, and returned as styled HTML
  5. Month navigation and view switching fetch lightweight HTML fragments — no full page reload

Embedding the Widget

Basic Embed

One line. That's it. Paste into any HTML page, Squarespace Code Block, or WordPress Custom HTML block:

HTML
<script src="https://api.pcowidget.com/v1/calendar-widget.js?key=YOUR_KEY"></script>
ℹ️

Replace YOUR_KEY with the API key from your PCOWidget account dashboard. Your key is provisioned when you sign up.

Squarespace

Add a Code Block (not a Markdown block) and paste your script tag. No <html>, <head>, or <body> tags needed — the widget handles everything.

💡

Cards layout width on Squarespace: Squarespace wraps code blocks in a .sqs-code-container element that can render as inline-block, preventing the card grid from filling the full column width. If your cards appear narrower than expected, add this to Design → Custom CSS:

.sqs-code-container {
  display: block;
  width: 100%;
}

WordPress

Add a Custom HTML block in the Gutenberg editor and paste the script tag.

Mounting Into an Existing Element

If you already have a container div where you want the widget, use data-target:

HTML
<div id="my-calendar"></div>
<script src="https://api.pcowidget.com/v1/calendar-widget.js?key=YOUR_KEY"
        data-target="my-calendar"></script>

Multiple Instances on One Page

Each <script> tag creates an independent widget instance. Configure each with different data-* attributes:

HTML
<!-- Full church calendar -->
<script src="https://api.pcowidget.com/v1/calendar-widget.js?key=YOUR_KEY"
        data-layout="table"></script>

<!-- Youth events sidebar -->
<script src="https://api.pcowidget.com/v1/calendar-widget.js?key=YOUR_KEY"
        data-tag="Youth"
        data-layout="list"></script>

<!-- This week's services (homepage) -->
<script src="https://api.pcowidget.com/v1/calendar-widget.js?key=YOUR_KEY"
        data-layout="simple"
        data-this-week="1"
        data-title="This Week's Services"></script>

Data Attributes

Control each widget instance using data-* attributes on the <script> tag:

ⓘ

Boolean attributes accept multiple formats. Any of "1", "true", "True", or "TRUE" are treated as on. Any of "0", "false", "False", or "FALSE" are treated as off. This applies to data-featured, data-this-week, data-allow-week-nav, data-include-past, data-hide-end-time, data-hide-if-empty, data-campus-untagged, and data-include-unpublished.

ⓘ

Only approved, published events are shown. The widget displays events that are approved in Planning Center and published to Church Center. Unpublished events, and anything still pending or rejected in your approval workflow, stay off your site. That is what you want for internal holds, tentative bookings, and staff-only items. If an event is missing from your website, check its publish setting in Planning Center first. See data-visibility below to build a staff-only preview widget that shows the levels underneath.

📄

Want to see every option in action? The live examples page shows all layouts, filters, and combinations running against real ELC data.

data-layout Default: table
Values: table · list · cards · tiles · week · simple
Initial view layout. table = month grid, simple = service tiles, tiles = compact image grid, others as named. See Layouts for details.
data-hover-previewNew Default: off
Values: true · 1
Tiles layout only. Tiles open the full event card on a click or tap. Set this to 1 to also open it when a visitor's pointer rests on a tile. Until October 2026 that was the default, and data-hover-preview="0" turned it off; an embed that still carries 0 keeps working and simply matches the default now.
data-tag Default: (none — all events)
Values: any PCO tag name, or comma-separated list for OR filtering
Filter to events with a specific Planning Center tag. Case-insensitive. Use a comma-separated list for OR logic — data-tag="Youth,Women's Ministry" shows events tagged either Youth or Women's Ministry.
data-campusNew Default: (none, every campus)
Values: a campus name, or a comma-separated list for OR filtering
Show only the events belonging to one or more campuses. data-campus="Downtown" gives that campus its own page; data-campus="Downtown,Westside" shows either. Matching is case-insensitive.

Leaving the attribute off shows every campus, which is what a single-campus church wants and never has to set. Nothing about an existing embed changes when you do not use it.

Where the campus comes from. Planning Center's Calendar has no campus field on an event, so the widget reads the Campus tag group you already keep in Planning Center: the tag names in that group are your campus names. Type them exactly as they read there, spelling included. A campus you add next year works the moment you tag an event with it, with no change to your embed code. If your group is named something other than "Campus", such as "Locations" or "Sites" or a name of your own, naming any campus in it is enough for the widget to find the rest.

This filters events. To control which tag chips are visible on those events, that is data-show-tags, and the two work together.

New to this? Multi-Campus Churches walks through setting campuses up in Planning Center, with links to Planning Center's own documentation.
data-campus-untaggedNew Default: off
Values: true · 1
Used alongside data-campus. Also keeps events that carry no campus tag at all.

Most churches leave their churchwide events untagged: the Christmas Eve service, the all-church workday, the annual meeting. Those belong on every campus page rather than none, and this is what puts them there. Left off, a campus page is strict and shows only what is tagged for that campus.

Decide it by what "untagged" means on your calendar. If untagged usually means a staff meeting or a room hold rather than a churchwide event, leave this off.

It has no effect on its own: with no data-campus set, nothing is being excluded in the first place.
data-this-week Default: (none)
Values: true · 1
Show only events in a rolling 7-day window starting from today. Designed for "This Week's Services" sections using the simple layout.
data-allow-week-nav Default: (none)
Values: true · 1
Adds a prev/next week toolbar so visitors can browse upcoming weeks. Works on the simple layout, and on any layout set to data-this-week. The prev button is disabled on the current week. The center label shows "This Week" / "Next Week" / "+N Weeks" and clicking it returns to the current week.
data-nav-align Default: center
Values: left · center · right
Alignment of the week navigation toolbar (simple layout only).
data-title Default: (none)
Values: any string
Override the section heading shown above the widget. E.g. data-title="This Week's Services" or data-title="Youth Events".
data-columns Default: 2 on simple, auto on cards
Values: 1 – 8
Tiles per row. On the simple layout this always applies and defaults to 2. On the cards layout it is optional: set it to pin an exact number of columns, or leave it off and the cards fit themselves to the width of whatever you dropped the widget into. Pinned counts above 2 step down to 2 columns on tablets, and everything collapses to 1 column on screens ≤480px.
data-featured Default: (none — all events)
Values: true · 1 · false · 0
Three-state filter: absent = all events (default); true/1 = featured events only; false/0 = non-featured events only. Featured events get star indicators and accent borders on card layouts.
data-limit Default: no limit
Values: 1 – 250
Maximum number of events to fetch per request. Leave it off and your widget shows every event in the month or week it is displaying, however busy that period is. Set it only if you want a deliberately short list.
data-rowsNew Default: no limit
Values: 1 – 100
Caps how many rows of events the widget renders, which is the simplest way to stop a busy month from running long down the page. On the list layout a row is one event. On the cards, tiles, and simple layouts a row is as wide as data-columns, so data-rows="2" alongside data-columns="3" shows six events. The table and week layouts ignore this, because their rows are calendar weeks rather than events.
data-shadow Default: blue
Values: blue · burgundy · standard
Container shadow color theme. See Shadow & Theming for visual examples.
data-accentNew Default: the standard blue
Values: a hex colour, e.g. #4a5a8c
Your brand colour. It replaces the widget blue everywhere: buttons, date badges, event times, today’s date, chips, and the event card. The hover shade, the pale tint behind chips, and the text colour on buttons are worked out from it, and a light colour such as gold automatically gets darker text so it stays readable. The shadow follows the colour too, unless you also set data-shadow. See Styling Your Widget.
data-accent-hoverNew Default: worked out from data-accent
Values: a hex colour
Only needed if you want an exact hover colour instead of the one calculated from data-accent.
data-fontNew Default: the widget font (Inter)
Values: inherit · a font name, e.g. Montserrat
inherit uses the font your website already uses where the widget sits. A font name uses that font, as long as your website already loads it.
data-frameNew Default: card with shadow
Values: flat · none
flat keeps the border and drops the shadow. none removes the border, shadow and white background so the widget sits directly on your page, which suits a white or light section. On a dark section, add data-mode="dark" as well.
data-cornersNew Default: rounded
Values: square
Square corners on the widget, buttons, cards, tiles and the event card, for sites with a squared-off look.
data-text-sizeNew Default: standard
Values: large
Larger event names, times, and event card titles, for sites with larger body text or an older congregation.
data-modeNew Default: light
Values: dark
For websites with dark pages. The widget, cards, tiles and event card turn dark with light text. Your data-accent colour still applies, with its shades lightened so text stays readable on dark; without one, the standard blue is adjusted the same way.
data-show-titleNew Default: shown
Values: 0
0 hides the heading above the widget (the tag name or your data-title), for pages that already have their own heading.
data-include-past Default: false
Values: true · false · 1 · 0
By default, events that have already ended are hidden. Set to true for archive or recap pages where past events should remain visible.
data-hide-end-time Default: (show end time)
Values: true · 1
Hide the end time from time displays, showing only the start time. Applies to simple, cards, and list layouts. Useful for service tiles where end times add visual clutter.
data-hide-if-empty Default: (show empty state)
Values: true · 1
When set, the entire widget container is hidden — no box, no border, no empty message, no space — if zero events match the current parameters. Without this flag the widget renders a "No upcoming events found" box. Useful for tag-filtered widgets on pages where that ministry may have no upcoming events.
data-empty-messageNew Default: "No upcoming events at this time."
Values: any text, up to 200 characters
Replace the wording shown when no events match. Useful for pointing visitors somewhere else, e.g. data-empty-message="Nothing scheduled this month. See our weekly service times below." Ignored when data-hide-if-empty is set, since nothing renders at all in that case.
data-show-tagsNew Default: (show every tag)
Values: a tag group name, an individual tag name, a comma-separated list of either, or 0
Controls which tag chips are visible on an event. Planning Center organizes tags into tag groups (Campus, Ministry, Category, and so on), and an event carrying six or eight tags pushes the rest of the card downward and looks cluttered.

Entries match either a tag group name or an individual tag name. Matching a group is usually what you want: data-show-tags="Campus" shows every tag in your Campus group and hides the rest, and a campus you add later appears automatically with no change to your embed code. Use data-show-tags="0" to hide tag chips entirely.

This is display-only. It does not change which events appear. To filter events by tag, use data-tag. The two can be combined: filter with data-tag, then tidy the chips with data-show-tags.
data-auto-advanceNew Default: off
Values: 1–6 (number of months to look ahead), or true for 1
When the month a visitor lands on has no matching events, jump forward to the first month that does, up to the number of months you specify. The month heading follows the jump, so the widget reads "September 2026" rather than claiming to show an empty August.

This solves the late-in-the-month problem: it is August 28th, the next event is September 3rd, and until now the widget showed an empty box. data-auto-advance="2" is a sensible starting point. Keep the number small: skipping five months ahead to surface one lonely event is about as useful to a visitor as showing them last month's calendar.

Auto-advance applies only to the month the visitor first lands on. Once they click the month arrows, the month they asked for is shown exactly, empty or not, because silently skipping past it makes the arrows feel broken.

This is not an aggregate window. It picks which single month to show. If you want the next few months of events gathered into one set with no navigation, that is data-months-ahead below.

Note: if you also use data-hide-if-empty, auto-advance runs first. The widget only disappears if no month within the lookahead window has events.
data-months-aheadNew Default: off
Values: 1–12 (months to gather)
Gathers every matching event from today out to this many months into one set, and removes the month navigation entirely. Where the rest of the widget answers "which month am I looking at", this answers "what is coming up", which is usually the right question for a home page.

It is a rolling window from today, not a run of calendar months: on 26 August, data-months-ahead="4" covers 26 August to 26 December. Finished events drop out as they pass.

Pairs naturally with data-tag and a data-rows or data-limit cap, which is how you get a fixed-height strip of the soonest events in a series.

Note: this replaces data-this-week and data-auto-advance rather than combining with them. All three choose a date window, and this one has already chosen it.

Layouts: list, cards, tiles and simple. table and week draw a calendar grid whose rows are the weeks of a single month, so they ignore it.
data-first-onlyNew Default: off
Values: tag name · tag group name · comma-separated list of either · all
Shows each repeating event once, at its next date, with a small +3 more dates note, instead of listing every date. The popover on tiles and simple lists the other dates; on cards and list they appear when you hover the note.

Name the tags it applies to, for example data-first-only="Men's Ministry, Women's Ministry, Children's Ministry", or use data-first-only="all" for every repeating event. Events without one of the named tags show every date as usual.

It works per event, not per tag. A weekly men's meeting collapses to one entry, and a one-off Men's Christmas Breakfast under the same tag still appears on its own.

"+3 more dates" counts the dates inside the window on screen: the month you are viewing, or the whole data-months-ahead window. data-limit and data-rows count distinct events.

Layouts: list, cards, tiles and simple. table and week are a calendar, which has to show every date, so they ignore it.
data-visibilityNew Default: published
Values: published · approved · pending · rejected
Planning Center tracks two separate things about an event: where it sits in your approval workflow (approved, pending, rejected) and whether you have published it to Church Center. This attribute walks down both at once, and each level includes everything the level above it showed:

LevelWhat appears
publishedApproved and published to Church Center. The default, and the right setting for a public website.
approvedEverything above, plus approved events you have not published yet.
pendingEverything above, plus events still waiting on approval.
rejectedEverything above, plus rejected events. In other words, your whole calendar.
Use the levels below published as a staging or preview tool. Put a second widget on a staff-only page with data-visibility="pending" and your team can see what is coming through the approval queue and check how it will look, while the public page keeps showing only finished, published events. Never set anything other than published on a page your congregation or visitors can reach.

If your church does not use Planning Center's approval workflow at all, every event counts as approved and only the publish setting matters. Nothing changes for you.
data-include-unpublished Default: published events only
Values: true · 1
Shorthand for data-visibility="approved": shows approved events whether or not you have published them to Church Center. If you set both, data-visibility wins. Prefer data-visibility in new embeds, since it says exactly how far you are going.
data-target Default: (auto-creates container)
Values: a DOM element ID
Mount the widget inside an existing <div> element by ID. If omitted, the widget creates and inserts its own container after the script tag.
data-askNew Default: off
Values: true · 1
For churches that also run the CESai Events Assistant on their website. Adds an "Ask about these events" button under the events that opens the assistant with a question about what this widget shows, for example "What is coming up for Women at Main Campus?" when the widget is filtered to that tag and campus. The button only appears on a page where the assistant is installed, so leaving it on elsewhere does no harm.
data-ask-textNew Default: "Ask about these events"
Values: text, up to 60 characters
The wording on the data-ask button. Has no effect without data-ask.
data-profileNew Default: off
Values: true · 1
Shows each visitor the events they care about. When a visitor has saved their interests, campus and days in the Events Assistant's "What matters to me" panel, the widget filters to those, with a "Showing events for: Women, Kids" line and a "Show all events" link. The preferences stay in the visitor's own browser and are never sent to PCOWidget or stored anywhere. This widget's own data-tag and data-campus always come first: on a women's ministry page, a visitor who chose Kids still sees the women's events. Visitors with no saved preferences see the widget exactly as before.

Quick Reference Table

AttributeValuesDefault
data-layouttable · list · cards · tiles · week · simpletable
data-hover-previewNewtrue · 1off (click opens)
data-tagtag name, or tag1,tag2 (OR)—
data-campusNewcampus name, or campus1,campus2 (OR)all campuses
data-campus-untaggedNewtrue · 1off
data-this-weektrue · 1—
data-allow-week-navtrue · 1—
data-nav-alignleft · center · rightcenter
data-titlestring—
data-columns1–82 simple / 4 tiles / auto cards
data-featuredtrue · 1 · false · 0—
data-limit1–250no limit
data-rowsNew1–100no limit
data-shadowblue · burgundy · standardblue
data-accentNewhex colourstandard blue
data-accent-hoverNewhex colourworked out from accent
data-fontNewinherit · font namewidget font
data-frameNewflat · nonecard with shadow
data-cornersNewsquarerounded
data-text-sizeNewlargestandard
data-modeNewdarklight
data-show-titleNew0shown
data-include-pasttrue · false · 1 · 0false
data-hide-end-timetrue · 1—
data-hide-if-emptytrue · 1—
data-empty-messageNewtext, max 200 chars"No upcoming events at this time."
data-show-tagsNewtag group name · tag name · list of either · 0show all tags
data-auto-advanceNew1–6 (months to look ahead)off
data-months-aheadNew1–12 (months gathered from today)off
data-first-onlyNewtag name · tag group name · list of either · alloff (every date shown)
data-visibilityNewpublished · approved · pending · rejectedpublished
data-include-unpublishedtrue · 1 (shorthand for approved)published only
data-targetelement ID—
data-askNewtrue · 1off
data-ask-textNewtext, max 60 chars"Ask about these events"
data-profileNewtrue · 1off

Boolean Values — Accepted Formats

The boolean attributes (data-featured, data-this-week, data-allow-week-nav, data-include-past, data-hide-end-time, and data-campus-untagged) are flexible about how you write true or false. All of the following are valid:

MeaningAccepted values
On / true"1"   "true"   "True"   "TRUE"
Off / false"0"   "false"   "False"   "FALSE"   (or omit the attribute entirely)

Examples — these are all equivalent:

HTML
<!-- All three turn on data-this-week -->
<script src="..." data-this-week="1"></script>
<script src="..." data-this-week="true"></script>
<script src="..." data-this-week="TRUE"></script>

<!-- All three turn on data-include-past -->
<script src="..." data-include-past="true"></script>
<script src="..." data-include-past="True"></script>
<script src="..." data-include-past="1"></script>

Layouts

Choose from six layouts, each designed for a different use case:

📅 table
Month GridFull month calendar with week rows. Best for event pages.
📋 week
Week StripCompact 7-day view. Best for sidebars and homepage sections.
📝 list
List RowsRow-based list with thumbnails. Best for ministry landing pages.
🃏 cards
Card GridRich image cards with descriptions. Best for featured events.
🖼️ tiles
Image TilesCompact image grid, opens the full card on hover. Best for a dense "what's on" block.
⏰ simple
Service TilesMinimal clickable tiles. Best for "This Week's Services" sections.

Month Grid — data-layout="table"

Full monthly calendar grid (Sunday–Saturday). Shows day numbers with event indicators.

  • Desktop: Event name and time visible inside grid cells
  • Mobile (<768px): Colored dot indicators instead of event text
  • Includes month/year header with prev/next month arrows and a view-switching toolbar
  • Click any event to open the full-detail popover
HTML
<script src="https://api.pcowidget.com/v1/calendar-widget.js?key=YOUR_KEY"
        data-layout="table"></script>

Week Strip — data-layout="week"

Compact 7-day view extracted from the month grid. Shows only the row containing today (or the first row with events for non-current months). Ideal for sidebars or "this week" widgets.

HTML
<script src="https://api.pcowidget.com/v1/calendar-widget.js?key=YOUR_KEY"
        data-layout="week"></script>

List — data-layout="list"

Compact row-based layout with thumbnail images (or date badges when no image). Shows event name, date, time, and location. Stacks on mobile. Good for ministry landing pages with moderate detail.

HTML
<script src="https://api.pcowidget.com/v1/calendar-widget.js?key=YOUR_KEY"
        data-layout="list"
        data-tag="Women's Ministry"></script>

Cards — data-layout="cards"

Rich card grid — auto-fit responsive layout. Each card is at least 260px wide; columns fill all available space naturally (typically 2 on tablet, 3+ on wide desktop, 1 on mobile). Each card shows:

  • 16:9 image (or date badge placeholder)
  • Date badge, title, time, description (3-line clamp), location
  • Tag color badges
  • "View Details" link
  • Featured events get accent borders and star indicators
HTML
<!-- Featured events showcase -->
<script src="https://api.pcowidget.com/v1/calendar-widget.js?key=YOUR_KEY"
        data-layout="cards"
        data-featured="1"
        data-shadow="burgundy"></script>

Image Tiles New – data-layout="tiles"

A compact image grid that sits between simple (text only) and cards (large and detailed). Each tile is about a third the height of a card: the event's image, its name on up to two lines, and the date and time. Built for churches that want a dense, visual "what's on" block instead of a long scroll of full cards.

Choose your grid. data-columns sets how many tiles go across, from 1 to 8, and defaults to 4. data-rows sets how many rows deep it goes. Together they give you an exact block: data-columns="5" with data-rows="2" is always ten events, no matter how busy the month is. Leave data-rows off and it shows everything. On smaller screens the grid steps down to 3 across, then 2, so tiles never shrink into illegibility.

<script src="https://api.pcowidget.com/v1/calendar-widget.js?key=YOUR_KEY"
        data-layout="tiles"
        data-columns="5"
        data-rows="2"></script>

Events without a picture still look right. Not every event in Planning Center has artwork, and on some calendars most of them do not. Those tiles get a coloured panel with a small calendar icon rather than a blank or broken image. The colour comes from the event's name, so the same event always looks the same and the grid stays varied instead of repetitive.

Clicking opens the full event. Click or tap a tile and the complete event card opens in the middle of the screen, with the large image, description, tags, location, and the "View Details", "Register", and "Add to Calendar" buttons. It works from the keyboard with Tab and Enter. Press Escape or click outside to close.

If you would like a tile to open when the pointer rests on it as well, add data-hover-preview="1". It waits for a brief pause first, so moving the mouse across the grid on the way somewhere else does not flash a card for every tile it passes over. Tapping on a phone opens the card either way, since phones have no hover.

Simple Tiles — data-layout="simple"

Minimal clickable service-time tiles. Each tile shows a clock icon, event name, sermon title (if set), date, and time range. Clicking a tile opens the full event popover. No toolbar — clean and focused.

Designed for use with data-this-week="1" to show the current week's events on your homepage or church info page.

Sermon Title Display

Your recurring "Traditional Worship" event stays the same in PCO, but the sermon title changes each week. To surface a sermon title on a simple tile, add a line to the PCO event's Details field:

PCO Event Details field
sermon: Giving Expectantly
  • The prefix (sermon:) is stripped — only the title text is shown
  • The match is case-insensitive (Sermon:, SERMON:, etc. all work)
  • The line can appear anywhere in the Details field
  • If no sermon: line is present, the subtitle is omitted — no blank space
💡

Pro tip: The sermon title line can appear anywhere in the event Details field — beginning, end, or mixed with other content. Just make sure it's on its own line.

Week Navigation

Add data-allow-week-nav="1" to let visitors browse upcoming weeks. The prev button is disabled on the current week. The center label shows "This Week" / "Next Week" / "+N Weeks" and clicking it returns to today.

Column Count

Use data-columns to control tiles per row. If you have 4 services on Holy Week, set data-columns="4". Always collapses to 1 column on mobile. The same attribute now pins the cards layout too, and pairs with data-rows when you want a fixed block of events rather than the whole month.

HTML — Full simple layout example
<script src="https://api.pcowidget.com/v1/calendar-widget.js?key=YOUR_KEY"
        data-layout="simple"
        data-this-week="1"
        data-title="This Week's Services"
        data-tag="Worship Services"
        data-allow-week-nav="1"
        data-nav-align="center"
        data-columns="2"></script>

Feature Flags

Feature flags are server-side controls managed by PCOWidget for your account. They determine which interactive features are enabled in your widget. All are enabled by default on Pro plans.

FlagFeatureEffect
WIDGET_POPOVER Event popover Click events to open a full-detail card (image, description, links, iCal)
WIDGET_TRANSITIONS Slide transitions Smooth left/right animation when navigating months
WIDGET_ICAL iCal downloads "Add to Calendar" links generate .ics files for Outlook, Google, Apple Calendar
WIDGET_TAG_COLORS Tag color coding Events colored by ministry category (see Tag Colors)
WIDGET_THIS_WEEK Week view option Enables the "Week" button in the toolbar; 7-day strip view
ℹ️

To request a feature flag change for your account, get support through our contact form.

Styling Your WidgetNew

Every church can match the widget to its website, on every plan. The quickest way is the Widget Display Builder: its Style your widget section has a colour picker and a live preview, and can show the preview on a light, grey or dark page. Each option below is also an attribute you can add by hand. Leave them all off and the widget looks exactly as it always has.

The widget also protects its own buttons from website themes. Page builders such as Elementor style every button on a page, which used to turn the widget’s month and view buttons into the theme’s buttons; they now keep the widget’s look.

What you wantAttribute
Your brand colour instead of the widget bluedata-accent="#4a5a8c"
Your website’s fontdata-font="inherit"
No card around the widget, so it sits on your pagedata-frame="none"
A border but no shadowdata-frame="flat"
Square cornersdata-corners="square"
Larger textdata-text-size="large"
No heading above the widgetdata-show-title="0"
A dark widget for a dark websitedata-mode="dark"

A worked example

A navy, square-cornered widget in the site’s own font, sitting directly on the page with no heading of its own:

<script src="https://api.pcowidget.com/v1/calendar-widget.js?key=YOUR_KEY"
        data-layout="tiles"
        data-accent="#4a5a8c"
        data-font="inherit"
        data-frame="none"
        data-corners="square"
        data-text-size="large"
        data-show-title="0"></script>

One colour in, a whole palette out

You choose one colour. The widget works out the rest: a darker shade for hover, a pale tint behind event chips and tags, a text shade that stays readable on white, and black or white text for anything drawn on your colour, whichever reads better. A pale brand colour such as gold therefore still gives legible times and buttons. If you want an exact hover colour, add data-accent-hover.

Your own CSS

You can still restyle the widget further with your own CSS: every widget class starts with pcow-. On the free Starter plan, the small “Powered by PCOWidget” line must stay visible. It takes on your brand colour when you set one, so it fits your page. Removing it is part of the Pro plan; see The Starter credit in our Terms.

Shadow Presets

Set the container shadow tone with data-shadow:

ValueVisualBest For
blue Blue-tinted drop shadow Default, works with most church sites
burgundy Warm red/maroon tinted shadow Traditional church aesthetics, warm palettes
standard Neutral gray shadow Minimal sites, any color scheme

CSS Isolation

All widget CSS classes are prefixed with pcow-. Widget styles will not conflict with your site's existing CSS, even on Squarespace, WordPress, or Wix where host-site styles are aggressive.

💡

Note: The widget deliberately avoids h1–h6 elements to prevent host-site heading styles from overriding widget typography.

Tag Colors

When tag color coding is enabled, events are automatically colored by their Planning Center tag. No configuration needed — the widget reads the tag name and applies the matching style.

Worship Services Blue — for weekend services and worship events
Adult Discipleship Green — for Bible studies, small groups, growth events
Youth Purple — for student ministry, youth group events
Children Amber — for children's church, family events
Outreach Pink/rose — for community, mission, outreach events

Tags not in the default map receive the standard blue styling. Additional tag color mappings can be configured on your account — contact support to request custom tag colors.

💡

Multiple tags: Events can have multiple PCO tags and all will be shown as color badges on card and list layouts. The data-tag attribute scopes which events are shown, not which tags are displayed. An event tagged "Youth" and "Outreach" shows both badges when you filter by data-tag="Youth".

Multi-Campus ChurchesNew

If your church has more than one campus, data-campus lets each campus page show only its own events, all from the one Planning Center calendar you already keep. One embed per page, one word different between them.

Where the campus comes from

Planning Center's Calendar has no campus field on an event. What it has is campus tags, and Planning Center creates them for you: add your campuses under your account settings and, in Planning Center's words, "campus tags are automatically set up in Calendar." They arrive as a tag group named Campus whose tags are your campus names, and those same tags become the campus filters your congregation sees on Church Center.

PCOWidget reads that group. So the campus names you put in data-campus are the campus names already in Planning Center, spelled the same way, and a campus you add next year works the moment you tag an event with it.

Setting it up in Planning Center

  1. Add your campuses in account settings. Do this first and Planning Center builds the Campus tag group in Calendar for you, so there is nothing to name or spell twice. (Planning Center: Campus tags)
  2. Tag your events with a campus. An event can carry more than one campus tag, which is how a service that runs at two campuses appears on both pages. (Planning Center: Organize events with tags)
  3. Consider making the Campus group required. Planning Center lets you mark a tag group required, which means an event cannot be created without one. That is what keeps a campus page from quietly going stale as staff add events and forget the tag. (Planning Center: Multi-campus setups)
  4. Add data-campus to that page's embed. Build it in the Widget Display Builder if you would rather not hand-write it.
⚠️

Campus pages look empty? Your events are probably untagged. Having campuses in your account settings is not the same as having tagged your events with them. If data-campus="Main Campus" returns two events when your calendar holds thirty, the filter is working and the calendar is the thing to fix: step 2 above. In the meantime data-campus-untagged="1" keeps the untagged events visible so the page is not bare while you catch up.

Churchwide events

Most churches leave the all-church items untagged: Christmas Eve, the annual meeting, the all-campus workday. Those belong on every campus page rather than none, which is what data-campus-untagged="1" does.

It is off by default, so a campus page is strict unless you ask otherwise. Decide by what untagged means on your own calendar. If an untagged event is usually a staff meeting or a room hold rather than something churchwide, leave it off. If you would rather be explicit, the alternative is to tag the churchwide events with every campus, which Planning Center allows and which makes them appear on each page without the attribute.

A worked example

Two campuses, Downtown and Westside. The Downtown page carries:

HTML
<!-- Downtown campus page -->
<script src="https://api.pcowidget.com/v1/calendar-widget.js?key=YOUR_KEY"
        data-layout="cards"
        data-campus="Downtown"
        data-campus-untagged="1"
        data-title="What's On at Downtown"></script>

The Westside page is the same embed with data-campus="Westside". A page that should cover both takes data-campus="Downtown,Westside", and the main church calendar page leaves the attribute off entirely and shows everything.

💡

Campus and tag chips are separate controls. data-campus decides which events appear. data-show-tags decides which chips are visible on them. On a single-campus page the campus chip is redundant, so data-campus="Downtown" data-show-tags="Ministry" is a tidy pairing: Downtown's events, with ministry chips and no campus chip.

ℹ️

Single-campus churches can ignore all of this. Leave data-campus off and every event shows, exactly as it always has. Nothing about an existing embed changes.

Responsive Breakpoints

The widget is mobile-first and adapts at these breakpoints:

< 480px
Compact grid cells, minimal spacing. Simple layout always 1 column.
< 640px
List: stacked layout (image above text).
< 768px
Month grid: colored dot indicators instead of event text in cells.
640px+
List: side-by-side (image left, text right).
Any width
Cards: auto-fit — fills available space with columns at least 260px wide. Typically 1 column on mobile, 2 on tablet, 3+ on wide desktop.

REST API Reference

The widget fetches data from PCOWidget's REST endpoint. You can call this directly for custom integrations or debugging.

Endpoint

GET https://api.pcowidget.com/v1/calendar-endpoint.php

Query Parameters

ParamValuesDefaultDescription
keystring(required)Your API key
formathtml · fragment · jsonhtmlResponse format
layoutcards · list · table · tiles · simplecardsTemplate layout (html/fragment only)
monthYYYY-MM(none)Explicit month; sets the date range for you
months_ahead1–12(none)One window from today out to N months, in your church's timezone
tagPCO tag name(none)Filter by tag (case-insensitive)
campuscampus name, or a comma-separated list(all)Events tagged with these campuses (your Campus tag group)
campus_untagged1(off)With campus, also keep churchwide events that carry no campus tag
daysNewsun…sat, comma-separated(every day)Events starting on these weekdays, in your church's timezone
featured1 · 0(none)Featured events only, or non-featured only
limit0–25010Maximum events returned; 0 means no cap (the widget always sends 0 unless data-limit is set)
rows1–100(no cap)Maximum rendered rows (list, cards, tiles, simple)
hide_past1(off)Leave out events that have already ended, before limit is applied
afterYYYY-MM-DD(none)Date range start
beforeYYYY-MM-DD(none)Date range end
filterfuture · pastfutureTime filter (suppressed when month is set)
columns1–82, or 4 on tilesColumns (simple and tiles always, cards when set)
visibilitypublished · approved · pending · rejectedpublishedHow far down the approval and publish ladder to look
first_onlytag or tag group names, comma-separated · all(off)One entry per repeating event, at its first date in the window. That entry gains more_dates and more_starts. Ignored by table and week
sourceNewcesai(a widget)Marks a CESai Events Assistant fetch: Pro plan, and not counted as a widget load

Response Formats

  • format=html: full HTML with wrapper div, scoped CSS, and feature flags JSON. Used for the widget's first load.
  • format=fragment: layout HTML only (no wrapper, no CSS). Used by the widget for month and view changes.
  • format=json: event data for custom integrations and assistants, with every time already in your church's timezone. See JSON Schema.

Example Requests

# Test your key — returns JSON event data
https://api.pcowidget.com/v1/calendar-endpoint.php?key=YOUR_KEY&format=json&limit=5

# March 2026 month grid as HTML
https://api.pcowidget.com/v1/calendar-endpoint.php?key=YOUR_KEY&format=html&layout=table&month=2026-03

# Youth events as cards
https://api.pcowidget.com/v1/calendar-endpoint.php?key=YOUR_KEY&format=html&layout=cards&tag=Youth

# Date range query
https://api.pcowidget.com/v1/calendar-endpoint.php?key=YOUR_KEY&format=json&after=2026-03-01&before=2026-06-30&limit=50

# Everything still to come in the next two months, as JSON
https://api.pcowidget.com/v1/calendar-endpoint.php?key=YOUR_KEY&format=json&months_ahead=2&hide_past=1&limit=0

JSON Schema

When using format=json, the response follows this structure. Every time is given twice: in your church's own timezone (start, start_date, start_time, weekday) and in UTC (starts_at), so nothing reading it has to convert anything. New fields may be added; existing ones keep their names.

JSON Response
{
  "status": "ok",
  "source": "widget",  // or "cesai" for the Events Assistant
  "timezone": "America/New_York",  // your church, from Planning Center
  "today": "2026-10-08",  // today where your church is
  "range": { "after": "2026-10-08", "before": "2026-12-08" },
  "count": 1,
  "truncated": false,  // true when a cap may have left events out
  "tag_groups": { "Ministry": ["Adult Discipleship", "Men's Ministry"] },
  "events": [
    {
      "id": "221064911",  // EventInstance ID
      "event_id": "20445373",  // Parent Event ID
      "name": "Men's Breakfast & Bible Fellowship",
      "start": "2026-10-10T07:30:00-04:00",  // your timezone, with its offset
      "end": "2026-10-10T09:00:00-04:00",
      "start_date": "2026-10-10",
      "start_time": "7:30 AM",  // null for an all-day event
      "end_time": "9:00 AM",
      "weekday": "Saturday",
      "all_day": false,
      "starts_at": "2026-10-10T11:30:00Z",  // UTC, as Planning Center sends it
      "ends_at": "2026-10-10T13:00:00Z",
      "tags": ["Adult Discipleship", "Men's Ministry"],
      "tag_groups": { "Ministry": ["Adult Discipleship", "Men's Ministry"] },
      "campus": [],  // tags from your Campus group, if you have one
      "excerpt": "This is an open group where all men are welcome...",  // plain text, up to 300 characters
      "description": "<div>...</div>",  // as written in Planning Center (HTML)
      "sermon_title": "",
      "location": "Emmanuel Lutheran Church",
      "image_url": "https://...",
      "church_center_url": "https://...",  // same as event_url
      "registration_url": "",
      "featured": false,
      "recurrence": "Weekly",
      "more_dates": 4  // only with first_only: further dates folded into this one
    }
  ],
  "meta": { ... }  // the request as the server read it, for debugging
}

Plans & BillingNew

Every church starts on the free Starter plan, with every feature and a small "Powered by PCOWidget" line under the widget. Pro removes that line, lifts the daily limit of 500 widget loads, and allows any number of websites. The current price is on the pricing section of the home page.

Upgrading to Pro

Use the Upgrade link in your welcome email, or the one on the page you saw after signing up. It opens a secure Stripe checkout for your account. Your API key and your embed code stay exactly the same: the credit disappears and the limit lifts on the next page load after payment.

Changing your card or cancelling

A self-service billing page is not available yet. Until it is, contact us and we will update your card or cancel your subscription for you. If you cancel, your widget keeps working on the Starter plan: nothing is removed from your website, and the "Powered by PCOWidget" line returns.

Already on Pro?

If an Upgrade link brought you here, your account is already on Pro and there is nothing to pay.

Troubleshooting

🔎

Check your embed automatically. Paste your whole <script> tag into Already have a widget? at the top of the Widget Builder. It lists anything misspelled, any value the widget cannot read (such as data-hide-if-empty="yes", which reads as off), a missing API key, and settings that have no effect, with a one-click fix for each. It also loads every setting so you can change one and copy the new code.New

Widget shows nothing

  1. Open your browser's Developer Tools → Console tab and look for JavaScript errors or failed network requests
  2. Verify the script src URL loads directly in a browser tab. It should return JavaScript, not an error page
  3. Test the endpoint directly: https://api.pcowidget.com/v1/calendar-endpoint.php?key=YOUR_KEY&format=json&limit=1
  4. Confirm your API key is correct and your account is active
⚠️

Squarespace: Make sure you're using a Code Block, not a Markdown block. Squarespace strips <script> tags from Markdown blocks.

CORS errors in browser console

If you see "Cross-Origin Request Blocked" or CORS errors:

  • Your site's domain may not be on the allowed domains list for your account
  • Get support through our contact form with your domain and API key to add it
  • example.com and www.example.com count as the same site, so listing either one covers both. Any other subdomain, such as events.example.com, needs its own entry
  • On Wix, the Embed HTML element runs on Wix's own file domain (for example www-example-com.filesusr.com). That is recognized as your site automatically, so there is nothing extra to add
  • Clear your browser cache after the domain is added

Events not showing / empty calendar

If you only just created or edited the event in Planning Center, see I changed something in Planning Center and the website hasn't caught up first. Otherwise:

  • Test the JSON endpoint directly to verify your PCO credentials are working
  • Confirm events exist and are published in Planning Center Calendar (not draft or hidden)
  • If using data-tag, verify the tag name matches exactly how it's spelled in Planning Center (matching is case-insensitive, but spelling must be exact)
  • If using data-campus, the same applies: the name has to match a tag in your Planning Center Campus tag group exactly. A campus page that comes back nearly empty is usually a campus whose events are not tagged rather than a broken filter. See Multi-Campus Churches
  • Check that events are in the date range — by default the widget shows future events only

The same weekly event fills my widget

A weekly or monthly event is one event in Planning Center with many dates, and the widget shows every date in its window by default. Over a few months that can mean the same meeting five or six times, pushing one-off events like a festival or a Christmas service out of view. Add data-first-only with the tags of those repeating events (or all) and each one shows once, at its next date, with a "+N more dates" note. The Widget Display Builder has this as Repeating events: show every date?

Event times look wrong

The widget shows every event in your church's own local time. It reads that timezone straight from your Planning Center organization settings, so there is no timezone attribute on the script tag and nothing for you to set on your website.

  • Open Planning Center and check the timezone recorded for your organization. If it is wrong there, it will be wrong on your site
  • Correct it in Planning Center. The widget re-checks the setting periodically and picks up the change on its own, with no edit to your embed code
  • If Planning Center has no usable timezone for your organization, the widget falls back to Eastern time rather than showing nothing
  • Times are stored in UTC by Planning Center and converted for display, so a single event never shows two different times on two pages of your site

Past events still visible

  • Make sure data-include-past is not set to "true"
  • Hard refresh the page (Ctrl+Shift+R / Cmd+Shift+R) — the widget JavaScript may be cached
  • If the issue persists, add a cache-busting query string to the script src: calendar-widget.js?key=YOUR_KEY&v=2

I changed something in Planning Center and the website hasn't caught up

This is expected, and it sorts itself out. Give it about a minute, then reload the page once. Changes you make in Planning Center reach your website within roughly 60 seconds.

The delay works in both directions, which covers the two most common surprises:

  • Something you added isn't there yet. A new event, a corrected time, a tag you just applied
  • Something you removed is still there. An event you rejected, cancelled, deleted, or unpublished from Church Center

Why it happens: your widget doesn't call Planning Center on every single page view. It reuses a recent copy of your calendar for up to a minute, which is what keeps the widget loading quickly for your visitors instead of making every one of them wait on an API call. Once that minute is up, the next page load fetches your calendar fresh.

ℹ️

Refreshing over and over won't speed this up. The timing is on our server, not in your browser, so a hard refresh, a private window, or a different device will all show the same thing. Wait about a minute, then reload once.

If two widgets on your site are showing different things for a moment, that is the same delay. Each widget keeps its own copy, so they can catch up a few seconds apart. They will agree again shortly.

Still wrong after two or three minutes? Then this isn't the refresh delay, and something else is going on. Check the event's publish and approval state in Planning Center, confirm any data-tag or data-campus spelling matches the tag exactly, and make sure the event falls inside the date range your widget is showing. See Events not showing / empty calendar above, or get support through our contact form and we will take a look.

Sermon title not showing on simple tiles

  • Confirm the line is in the Details field of the event in Planning Center Calendar (not the title or notes on a separate app)
  • The line must begin with sermon: (with a colon) — no space before the colon
  • The line can appear anywhere in the Details field, but must be on its own line
  • Sermon titles only display on the simple layout
💬

Need help? Get support through our contact form, and include your API key along with a description of the issue. Pro plan users receive priority response.