Stable release. Marks the end of the v4.3 beta cycle (beta1 → beta6).
Read full notes12 changes
Stable release. Marks the end of the v4.3 beta cycle (beta1 → beta6).
Vote integrity rewrite: every vote stores a voter_hash (poll + IP + UA + cookie + user) and dedupe queries that one column regardless of mode. Cookie set on render with SameSite=Lax; Secure, persists across AJAX. vote_limit=user rejects anonymous voters at submit. Honeypot on by default, per-IP rate limiter (12/minute).
Scheduling (start date + hourly cf_poll_auto_close cron), change-vote window, Gutenberg block (core-forms/poll) with picker + display-mode, opt-in live results (15s, pauses on hidden tab), bar / columns / percent result styles, container-query-driven adaptive layout, REST API (/cf/v1/polls, /polls/{id}/results public, /polls/{id}/analytics auth), per-poll analytics screen + CSV export.
Accessibility: <fieldset><legend>, role="progressbar" with aria-valuenow, role="status" aria-live="polite", focus moves to result heading on submit, prefers-reduced-motion honored. jQuery removed from poll JS.
Schema migration (cf_db_version → 4.3.0) is additive with backfill so historical dedupe keeps working.
All three plug into the existing Core Forms → Import page via a new cf_import_sources filter + cf_import_render_section action exposed by CF7Migration, with shared helpers in a BaseMigration abstract class.
Gravity Forms — GFAPI-driven, ~14 field types, notifications → Email actions with per-recipient conditions, confirmations → success message / redirect, conditional logic → data-show-if / data-hide-if. Field IDs resolve through a per-form name map so condition references stay valid.
WPForms (Lite + Pro) — Decodes the form JSON, maps notifications, optional entry import from wp_wpforms_entries (Pro) into wp_cf_submissions so admin history survives the cutover.
Fluent Forms — Reads wp_fluentform_forms + wp_fluentform_form_meta directly (no plugin dependency), maps fields, notifications, webhooks, and conditional logic.
Send Email — conditional emails (per recipient): New "Recipient routes" repeater on the Send Email action. Each route has a field + operator + value + recipient address; the first matching route's recipient overrides the default To. The existing action-level conditions block keeps working — that gates whether the action runs at all; routes only re-target where it sends.
Forms list: Fix for the Duplicate row action: it used to drop the user into the editor for the original form because the URL carries a form_id and the overview page short-circuited into the editor before the list table's single-row handler could run.
Migration is automatic on next admin pageload (or on plugin update via upgrader_process_complete).
New WP-Cron hook cf_poll_auto_close is scheduled hourly; unscheduled on deactivation.
New post meta keys for importer provenance: _cf_gf_source_id, _cf_wpforms_source_id, _cf_fluent_source_id — useful for debugging or re-import workflows.
Duplicate row action now duplicates the form. Clicking Duplicate on the forms list used to drop the user into the editor for the original form because the duplicate URL carries a form_id and the overview page short-circuited into the editor…
Read full notes2 changes
Fixes
Duplicate row action now duplicates the form. Clicking Duplicate on the forms list used to drop the user into the editor for the original form because the duplicate URL carries a form_id and the overview page short-circuited into the editor before the list table could run its single-row action handler. The overview now processes action=duplicate_single first, clones the form, and redirects back to the list with ?duplicated=1.
Also: fixes two pre-existing PHPStan warnings that were blocking the release pipeline.
Read full notes5 changes
Container builder polish
Add Field popover redesigned. Fixed 360px width with a 4-column icon grid, header bar, and a dedicated close button. Dismisses on outside-click or Escape.
Containers default to a single column. New Containers ship as plain wrappers; opt into a grid via the Layout panel when you actually want one.
Column changes preview in real time. The canvas duplicates the .cf-row grid rules locally, so editing column counts updates the preview immediately.
Better drop-target affordance. Empty containers and group bodies show a dashed outline at rest and a solid blue highlight while dragging.
Selecting a Container on the Fields tab now reveals a Layout section in the inspector sidebar with the column controls. Three column-count inputs (desktop / tablet / mobile, 1–6 each) plus a free-text gap field. Tablet and mobile inherit…
Read full notes4 changes
Patch: Container field column controls in the inspector sidebar: Selecting a Container on the Fields tab now reveals a Layout section in the inspector sidebar with the column controls. Three column-count inputs (desktop / tablet / mobile, 1–6 each) plus a free-text gap field. Tablet and mobile inherit from the next breakpoint up when left blank.
Settings persist on the field and emit as inline CSS variables (--cf-cols, --cf-cols-t, --cf-cols-m, --cf-gap) on the saved .cf-row wrapper, so the runtime form picks them up via the existing rules in forms.css.
The canvas now applies the same CSS variables to the container's drop area, so the drag-and-drop preview mirrors the runtime grid instead of stacking fields vertically.
New Containers ship with cols_desktop: 2, cols_mobile: 1, gap: 1rem by default — a useful starting point you can override per breakpoint.
"Core Polls" no longer registers its own top-level menu item. Polls now live as two submenu entries (Polls + Add Poll) under the Core Forms menu, so the admin sidebar reads as one product instead of two side-by-side icons.
Read full notes4 changes
Patch: Polls moved under the Core Forms admin menu: "Core Polls" no longer registers its own top-level menu item. Polls now live as two submenu entries (Polls + Add Poll) under the Core Forms menu, so the admin sidebar reads as one product instead of two side-by-side icons.
PollAdmin::add_admin_menu() switched from add_menu_page to two add_submenu_page calls under the core-forms parent.
admin_menu hooked at priority 20 so Admin::menu() (default priority 10) has already registered the parent slug before Polls tries to attach.
New parent_file filter keeps the Core Forms top-level item highlighted (and selects the right submenu sub-item — Polls list vs Add Poll) while editing, creating, or listing polls. Without it, the WP post-type screens would blank out the sidebar's active state.
The Preview button that landed on the Add Form gallery in 4.2.4 now also appears on the template picker that opens inside the per-form Fields tab (the React visual builder).
Read full notes4 changes
Patch: preview button on the per-form template chooser: The Preview button that landed on the Add Form gallery in 4.2.4 now also appears on the template picker that opens inside the per-form Fields tab (the React visual builder).
Each non-blank card carries a "Preview" pill in its top-right corner. Hovering or focusing the card reveals it; clicking it opens the same iframe modal the Add Form gallery uses. event.stopPropagation stops it from also firing the parent "select template" click.
The preview modal moved from inline page markup into Admin::render_template_preview_modal, hooked on admin_footer for every Core Forms admin screen. Click handling is delegated — any element with data-cf-preview="{slug}" (anywhere on a Core Forms page) fires it. window.CoreFormsAdmin.openTemplatePreview(id, label) is exposed for programmatic triggers.
Localized cfFormTemplates entries now carry a preview_url so the React builder (or any other JS) doesn't have to re-derive the admin-ajax URL.
Three fixes / one-line connections that were missing in 4.2.4.
Read full notes5 changes
Patch: template preview + Create Comment surface + default actions: Three fixes / one-line connections that were missing in 4.2.4.
Template preview iframe now actually loads. The wp_ajax_cf_template_preview action was being registered on the Admin class constructor, which is gated behind ! DOING_AJAX — so the AJAX handler was never wired during AJAX requests. WP's admin-ajax fell through to its default "0" output and the iframe showed a blank page. Registered the handler in the AJAX branch separately with a lazy-constructed instance.
Create Comment action now appears in the Actions tab picker. The action class registered fine via cf_available_form_actions, but the WordPress category in tab-actions.php didn't list create_comment in its types array — so the grouped grid skipped it. Now slotted in next to create_user/create_post with an admin-comments dashicon.
Every starter template lands with a default Email action wired. Previously a brand-new form had no actions at all — submissions saved to the inbox but never emailed anyone. process_create_form() now appends a default "Email the admin" action when the template doesn't ship its own. Templates that DO ship actions (product_review → Create Comment, payment templates → providers) keep their wiring untouched.
Product review + Lightweight comment templates ship with Create Comment pre-wired. Field mappings — NAME → comment author, EMAIL → author email, REVIEW/COMMENT → content, RATING → rating meta — plus a default Email notification. Open the form, pick a target post on the action, save — done.
This release adds a proper review/comment workflow (star rating field + a Create Comment action that writes to WordPress comments with aggregate rating meta), five new starter templates with a sandboxed preview iframe on the Add Form…
Read full notes20 changes
Star ratings, comments, and a much richer admin: This release adds a proper review/comment workflow (star rating field + a Create Comment action that writes to WordPress comments with aggregate rating meta), five new starter templates with a sandboxed preview iframe on the Add Form gallery, a column-layout container with responsive breakpoints, real-time preview sync in code mode, and a bunch of admin clarity fixes.
Star rating + comments
New .cf-rating field type — radio-based 1–5 star picker with CSS-mask icons inlined as data URIs (works whether or not the form theme is loaded). Keyboard-reachable with per-star screen-reader labels. Reverse-ordered radios + the adjacent-sibling cascade fill stars to the left of hover/check.
New Create Comment / Review action — posts the submission as a WordPress comment against a target post (numeric id, [field] reference, or auto-detected via referer URL). When a rating field is configured, stores the value as comment meta rating and recomputes the target post's cf_rating_average + cf_rating_count post meta — Schema-rich-snippet plugins read these keys by convention. Status flips (pending → approved) trigger a recompute via transition_comment_status.
Five new starter templates: | Template | Use | |---|---| | Product review | Star rating + headline + comment, paired with the Create Comment action | | Lightweight comment form | AJAX-submitted drop-in for the native WP comment form | | NPS / satisfaction survey | 0–10 scale + reason + optional email | | Bug report | Severity, browser, steps, attachment — two-column layout | | Product waitlist | Email + company + source, two-column layout |
Template preview iframe: Every non-blank template card on the Add Form gallery now has a Preview button that opens a modal iframe loading the markup via wp_ajax_cf_template_preview. Capability-gated to edit_forms, loads the same forms.css + form-theme.css a real form would use, submit-disabled so nothing fires.
Columns layout container (.cf-row): CSS helper driven by inline CSS variables — --cf-cols, --cf-cols-t, --cf-cols-m, --cf-gap — sets desktop / tablet (≤1024px) / mobile (≤640px) column counts up to 6 each. Variable chaining means setting only the desktop count cascades to tablet and mobile. Code-mode inserter prompts for the three numbers and drops the wrapper in.
Real-time preview sync: Code-mode textarea now pushes its current markup to the preview iframe on every keystroke (180ms debounce) via postMessage. The iframe replaces #cf-preview-root innerHTML in place. Origin- and form-id-scoped so a cross-form iframe can't be poisoned.
Form editor preview honours the global stylesheet toggles: The admin asset enqueue now conditionally loads forms.css and form-theme.css (with cf_form_theme_tokens overrides) when you're on a form-edit screen, so the visual builder canvas and code-mode iframe both render with the same stack the front-end uses. Previously the editor preview was unstyled even with the theme on globally.
Settings clarity
Skeleton CSS vs Default form theme — both options renamed with explicit descriptions of what they load. Skeleton (forms.css, ~1.5 KB, structural only — labels, error states, button alignment, the new column-row helper). Theme (form-theme.css, opinionated visual layer — colors, radii, focus rings, typography). Choices are "Load / Skip".
After Submission — clearer two-way choice ("Show success message" / "Redirect to a URL"). Contextual help appears with the selection. The redundant "Redirect to a page" option was removed; the URL field already covers that case.
Per-form Settings tab now opens with only the first accordion (Submissions) — previously two opened simultaneously.
"Add-ons" → "Extras" in Settings + Messages section labels.
Action merge: CreatePost + CreateArticle: The richer CreateArticle (title/content/excerpt mapping, taxonomies, ACF, featured image, author lookup) now claims the canonical create_post slug and the label "Create Post / Article / CPT". Forms saved with the old create_article type are forwarded to the merged handler at runtime — no migration.
Bug fixes / hardening
vendor/autoload.php require is now file_exists()-guarded so a partial extraction during an in-place plugin upgrade can't fatal the site. Clear admin notice surfaces on the next page load if vendor/ is genuinely missing.
Template preview wp_die() calls now pass HTTP codes via the response arg instead of as the title. Unauthorized hits actually return 403/400/404.
CreateComment recompute only fires when a valid in-range rating actually landed as comment meta; out-of-range values skip the recompute.
Adds a CF_DEMO_MODE wp-config constant guard so the public demo build at core-forms.com/demo can be safely handed out via token-gated R2 — the plugin self-disables anywhere except playground.wordpress.net subdomains. No-op for production…
Read full notes
Adds a CF_DEMO_MODE wp-config constant guard so the public demo build at core-forms.com/demo can be safely handed out via token-gated R2 — the plugin self-disables anywhere except playground.wordpress.net subdomains. No-op for production sites.
What was wrong: The bundled embed.js (served from /wp-json/core-forms/v1/embed.js) was rendering result.data.message directly into the success card. But the form processor returns it as an object — { type: 'success', text: '...' } — not a string. JavaScript stringified the object to literal [object Object], so every successful headless submission ended with that string in the success card. Same shape bug in the error path.
What's fixed: Added a pickText() helper that: - Plucks .text when the message is an object. - Returns the value when it's already a string (for forward-compat). - Falls back to a sensible default (Thanks! / Submission failed.) when the field is missing.
Who's affected: Anyone running the drop-in embed widget (the `
Per-form spam view routes correctly: ?page=core-forms&view=spam&form_id=X&tab=submissions now opens the form-edit screen on the Submissions tab in spam-filter mode instead of falling through to the all-forms list. The router branched on view=edit; it now branches on the presence of form_id so any view value (spam / all / inbox) routes correctly.
Submissions list — visual cleanup: Dropped the 3px blue / red inset left border on new and spam rows and the muted grey text on spam rows. Status is communicated entirely by the badge in the STATUS column (New / Read / Replied / Spam) — much cleaner against WP's native striped table.
Dashicon alignment fix for WP < 7.0: Buttons that pair a dashicon with text (Documentation, Refresh, Open, copy buttons in the Headless tab) had the icon glyph sitting ~2px above the text baseline on WordPress 6.9.x and earlier. WP 7.0 normalized this natively; for older WP a high-specificity rule scoped to Core Forms admin screens applies vertical-align: middle, line-height/font-size resets, and :has(.dashicons) inline-flex centering to match.
Stable release. Consolidates eleven betas (v4.2.0-beta through v4.2.0-beta11) into one shipping version.
Read full notes31 changes
Stable release. Consolidates eleven betas (v4.2.0-beta through v4.2.0-beta11) into one shipping version.
Both Cloud API and Twilio sends log through Email Logs. The cf_form_redirect_url filter now fires on every successful submission and receives the Submission as a 4th argument.
WhatsApp, three ways: Three new actions cover the full range of WhatsApp delivery models — pick whichever fits your existing stack.
WhatsApp Cloud API action. Send template messages and free-form text (24-hour window) via Meta's Graph API. Free tier covers 1,000 conversations per month. Credentials live globally under Settings → WhatsApp Cloud API with per-action override; a "Test connection" button reports the verified business name + quality rating.
Twilio WhatsApp action. For shops already on Twilio — reuses your Account SID / Auth Token, posts to the whatsapp: channel, supports approved Content Template SIDs.
Click-to-WhatsApp redirect. Generates a wa.me deep link with the submission pre-filled into the chat draft, used as the post-submit redirect.
Contact Form 7 → Core Forms migration tool: New page under Core Forms → Import that scans every wpcf7_contact_form post, converts shortcode markup to plain HTML (14 tag types covered), maps the CF7 mail template to a Send Email action, and carries messages overrides across as cf_message* post meta. Per-row preview lets you sanity-check before committing. Gravity Forms, WPForms, Fluent Forms, Ninja Forms, Formidable previewed as "Coming soon" cards.
Per-form analytics dashboard: New Analytics tab on every form-edit screen, powered by the existing cf/v1/analytics/* REST API: - One "Funnel" postbox with views, starts, submissions, spam. - SVG daily-activity chart with all three series overlaid. - Field interactions panel (where users focus/fill). - Top sources panel (UTM attribution with referrer-domain fallback). - Auto-generated recommendations — high spam rate, low completion, drop-off hotspots. - Date-range presets (7d / 30d / 90d / 1y) + custom from/to.
Form versioning + change history: The core-form post type now declares 'supports' => […, 'revisions', …]. cf_settings, _cf_hidden_columns, and every cf_message override persist alongside each revision. Settings-only or messages-only saves still create a revision (we force wp_save_post_revision_check_for_changes to false for core-form). New Revisions* tab lists each snapshot with author, timestamp, and a diff signal — restore drops back into the Core Forms admin with a flash and copies meta back automatically.
Security hardening — 14 audit findings fixed
CSRF on by default (existing installs preserved via 4.2.0 migration).
Submission mass-assignment closed via markup-derived allowlist.
Headless API key header-only; new CORS allowlist textarea.
Button styling unified — removed the .button-primary glow override that broke consistency.
Fullscreen / Typeform-style display mode promoted to its own top-level card on Settings.
WhatsApp actions surfaced in Add Action picker under new "Messaging & SMS" category.
Per-form Template picker unified with Add-New-Form gallery — all 12 templates available everywhere.
Headless [CF_REFERRER_URL] fix: Cross-origin Referer headers are stripped to the bare origin by browsers. The submit endpoint now reads _cf_page_url from the JSON body and substitutes it into $_SERVER['HTTP_REFERER'] for the duration of Forms::process(). Bundled embed.js forwards window.location.href automatically.
Build pipeline: Production builds run through bash build.sh which executes composer install --no-dev so dev-only autoload entries can't leak. 148 unit tests pass; PHPStan clean against regenerated baseline; production zip 741KB.
Upgrade: Download the attached zip and upload via Plugins → Add New → Upload. Existing data, settings, and forms are preserved. The 4.2.0 migration runs automatically on first admin pageload after upgrade.