Core Forms 4.3
Polls rebuilt from the database up, importers for Gravity Forms, WPForms and Fluent Forms, conditional recipients on the Send Email action, and eight patch releases through June 7 covering licensing, asset loading, accessibility and a critical-error fix.
Core Forms 4.3 is out as of May 17, 2026. The change most people will notice is polls, which were rebuilt from the database up: a tabbed editor, a Gutenberg block, scheduling, live results, a change-vote window and a rewrite of how votes are counted so the same person cannot vote twice. The second change is three new importers, so a site on Gravity Forms, WPForms or Fluent Forms can move over without retyping a form.
This post covers the release and the 8 patch releases that followed it through June 7. Two of those matter more than the release itself if you run the plugin on many sites: 4.3.7 fixes a critical error after any plugin update, and 4.3.8 fixes a packaging mistake that stopped the smart-forms script from loading at all. Forms you already have are not touched. The polls tables get a migration, and the next section says what it touches.
The update is under Dashboard, then Updates. The changelog has the raw list, and the support page is the place for anything this post gets wrong.
Before You Update
If you are reading this after June 7, update straight to 4.3.8. The reason is in the 4.3.7 note further down.
The polls schema migrates to cf_db_version 4.3.0. It adds type, starts_at, allow_other, post_close_behavior, redirect_url, enable_live_results and change_vote_window to wp_cf_polls, and option_value, voter_hash and referrer to wp_cf_poll_votes with a composite index on poll and voter hash. Existing rows are backfilled, so historical dedupe keeps working.
Two poll defaults changed. A poll with vote_limit=user now rejects anonymous voters at submit; before, it silently let them vote without limit. The honeypot is on by default for polls, and a per-IP limiter allows 12 votes a minute.
What’s New in Core Forms 4.3
Three areas changed, and polls are the largest piece of work in any 4.x release so far. The other two are about getting forms in and mail out.
Polls
The old metabox screen is gone. A poll now has its own tabbed editor and its own block, and the front end was rewritten without jQuery.
Editor:
- Question, Settings, Schedule, Embed and Results tabs.
- Drag to reorder options, a
typeselector for single or multiple choice, an inline “Other” write-in toggle and a Duplicate row action.
Vote integrity:
- Every vote stores a
voter_hashbuilt from the poll, IP, user agent, cookie and user, and dedupe queries that one column whatever the configured vote-limit mode. - The cookie is set on render rather than on the first POST, with
SameSite=Lax; Secure, and it persists across AJAX requests. vote_limit=userrejects anonymous voters at submit. The honeypot is on by default and rate-limited so a tripped honeypot cannot be used to flood the endpoint. A per-IP limiter allows 12 votes a minute.
Scheduling and after close:
- A start date beside the existing end date. An hourly
cf_poll_auto_closecron flips the status to closed once the end time passes. - Post-close behavior is a setting: show the final results, hide the poll, or show a notice with a redirect link.
- A per-poll change-vote window, in minutes, re-renders the form pre-checked so the same voter can update a choice.
Block and display:
- A
core-forms/polldynamic block with a poll picker and a display mode of auto, results only or form only. The stylesheet registers as bothstyleandeditor_style, so the editor preview matches the front of the site, and the server render shares code with the shortcode. - Opt-in live results, polling every 15 seconds and pausing while the tab is hidden.
- Result styles: horizontal bars by default, vertical columns or percent only.
- An adaptive card that sizes its padding, radius and type scale to the container’s width with container queries, not the viewport. Options flow into a multi-column grid when the container has room, with a viewport
@mediafallback for browsers without container query support.
REST and analytics:
- A public
GET /cf/v1/polls/{id}/resultswith vote counts and the total, and authenticatedGET /cf/v1/polls,/polls/{id}and/polls/{id}/analyticswith a per-day series. - A per-poll analytics screen with totals, unique voters, a 30-day SVG line chart, an option breakdown table and a CSV export of raw votes with the option label, value, voter hash, IP, user, timestamp and referrer. The page registers without a parent slug so the
edit_postscapability is honored for editors.
Accessibility:
<fieldset><legend>around the question,role="progressbar"witharia-valuenowon each result bar androle="status" aria-live="polite"on the message region.- Focus moves to the result heading after a successful vote, and
prefers-reduced-motionturns off the bar animations.
Importers for Gravity Forms, WPForms and Fluent Forms
The Import page that arrived with the Contact Form 7 tool now offers three more sources.
Gravity Forms reads forms through GFAPI and converts about 14 field types to HTML. Every notification becomes a Send Email action, with per-recipient conditions when the source notification had its own conditional-logic block. The default confirmation maps to the success message or redirect URL, and field-level conditional logic carries across as data-show-if and data-hide-if attributes. Field IDs resolve through a per-form map from field ID to rendered name, so condition references stay valid after the move.
WPForms, Lite and Pro, decodes the form JSON stored in the wp_wpforms post content, converts the field map to HTML and turns each notification into an Email action with per-recipient conditions. When the Pro entries table is present, the importer can also bring stored entries into wp_cf_submissions, so the admin history survives the cutover. The success notice shows how many entries came across.
Fluent Forms reads wp_fluentform_forms and wp_fluentform_form_meta directly, with no dependency on the Fluent API, which means the importer works even when the source plugin is deactivated. It converts the field map, walks the notifications meta for Email actions, picks up webhook-shaped meta as Trigger Webhook actions and maps the default confirmation. Conditional logic carries across.
There is an extension point behind all of this. The source picker is driven by a cf_import_sources filter and a cf_import_render_section action, and a third-party migrator plugs in through the shared BaseMigration abstract class. Default field names always carry an _<id> suffix, so two source fields sharing a label produce distinct names.
Conditional Email Recipients
The Send Email action gains a Recipient routes repeater. Each route is a field, an operator, a value and a recipient address. The first matching route wins and the default To address is used when nothing matches.
This is separate from the action-level conditions block, which still decides whether the action runs at all. Routes only change where it sends. Both use cf_evaluate_action_conditions(), so the operators behave the same in both places.
Smaller Changes and Fixes
- About 80 formatting issues across the form-builder JavaScript were fixed under ESLint,
!= nullchecks were rewritten as explicit dual checks, and the remaining lint exceptions are documented in place. - The PHPStan baseline was regenerated. All three migrators and the polls rework are type-clean at level 5, and the unit tests pass on PHP 8.1, 8.2 and 8.3.
The Patch Releases
Eight patch releases followed over 3 weeks. Read 4.3.7 and 4.3.8 even if you skip the rest.
4.3.1
May 22, 2026. Licensing and updates moved to the CF Licenser on core-forms.com as a drop-in replacement for the previous FluentCart-backed manager. License keys, the options shape and the admin UI stayed compatible, so a 4.3.0 install upgraded with nothing to re-enter. The heartbeat runs every 6 hours instead of weekly, update checks use a 6-hour cache and the “Check for updates now” button always returns a fresh answer. Update metadata carries the release’s SHA-256, generated when the ZIP is uploaded, as an integrity reference. The server is multi-product and provider-agnostic from the start.
4.3.2
June 2, 2026. forms.js, accessibility.js, analytics.js and the form styles load only on pages that render a Core Forms block or shortcode. The redundant block-level viewScript metadata is gone, since rendered forms enqueue the runtime directly. Tests cover the detection, including a page that has a poll and no form.
4.3.3
June 5, 2026. Opening a submission no longer throws a database error about a missing wp_cf_submission_replies table. The reply-history feature queried a table no installer or migration had ever created. It is now created on activation, on new-blog insert, on multisite activation and backfilled by a migration, so existing sites heal themselves on update. The “Not Spam” bulk action on the per-form submissions table now fires the form’s success and configured actions that were skipped at submit time; the spam page’s single and bulk paths already did this, so the replay is centralized in cf_replay_submission_actions() and the three paths cannot drift apart again.
4.3.4
June 6, 2026. cf_get_form() is memoised for the length of the request, so the same form asked for by the block render, get_html, the action loop and the analytics tracker is fetched once and returned as a clone. The cache invalidates on save_post_core-form and the post-meta hooks, gated by post type so other post types do not clear it. cf_count_form_submissions() is memoised the same way. The forms list used to issue one COUNT query per row, so a 50-form admin ran 50 queries; it now runs one per form and spam-filter pair. The block-editor preview shows a skeleton shaped like a form while it loads, respecting reduced motion, and pressing / or s on the forms list focuses the search box unless a field already has focus.
4.3.5
June 6, 2026. The block editor was still loading a legacy pre-4.0 script, gutenberg-block.js, which registered core-forms/form with a bare “CF” icon and a single dropdown, overriding the block.json registration. That is why the block UI regressed in 4.3.3 and 4.3.4 even though the new bundle shipped correctly. The legacy enqueue and the orphan file are gone, block.json owns the editor, and every block asset now cache-busts against the plugin version instead of the WordPress version. The form picker label went back to “Title (slug)” so forms sharing a base title stay distinguishable.
4.3.6
June 6, 2026. Accessibility and cascade work:
- Every text input, select, textarea and button inside a Core Form has a
min-heightof 44px, the WCAG 2.5.5 AAA touch-target size, applied in both stylesheets so the floor holds whichever one you load. - The keyboard focus indicator survives Windows High Contrast Mode. The themed
:focus-visiblepairs the colored box-shadow with a transparent outline; HCM strips the box-shadow but forces the outline to the system focus color. forms.cssships a small baseline of its own, the 44px target, acurrentColorfocus outline and a reduced-motion kill switch, so a site that turns the theme off still gets an accessible form rather than a bare browser stylesheet.- Textareas are 5 lines tall by default instead of the browser’s 2, in
lhunits with a 140px fallback, so the height tracks the computed line height. - Form and poll stylesheets enqueue at priority 99 instead of 20, so they print at the bottom of
<head>after the host theme’s styles. No!important, no specificity escalation. Poll CSS used to land in the footer because it was enqueued duringthe_content; polls are now detected ahead of time and the stylesheet lands in<head>on poll pages only.
4.3.7
June 6, 2026. A critical error appeared after any plugin, theme or core update or rollback. The migration callback hooked to upgrader_process_complete was registered under the plugin namespace while the function lives in the global namespace, so WordPress called an invalid callback every time an upgrade completed. The name is corrected and updates finish cleanly. This is the reason to go straight to 4.3.8 from anything earlier.
4.3.8
June 7, 2026. Field names in the builder accept uppercase letters such as FirstName; the Name field had force-lowercased every keystroke even though HTML name and id attributes are case-sensitive. Textareas gain Rows, a Resize setting (default, vertical, horizontal, both, none) and a live character counter when a max length is set. Phone fields gain Min length, Max length and Pattern. A builder bug that dropped min-length, max-length and conditional-logic attributes from textareas on save is fixed.
The packaging fix is the one to know about. The smart-forms front-end script, which carries inline validation, autosave and resume, multi-step navigation, calculations and the new character counter, lived under assets/js/src/ and was stripped from the distributed ZIP, so it never loaded on installed sites. It now ships from assets/js/smart-forms.js, and the release build includes block.json and render.php so the Gutenberg block registers correctly in CI-built packages.
The Limits
Live results poll every 15 seconds. They are not pushed, so a results screen on a wall display can lag by that much.
The importers read fields, notifications, confirmations and field-level conditional logic. That is the list. Entries come across from WPForms Pro only; Gravity Forms and Fluent Forms entries stay where they are.
A recipient route changes the To address and nothing else. CC, BCC and the subject line follow the action’s settings for every route.
Docs and Links
- Core Forms 4.3 in the changelog, with every patch beneath it.
- Polls on the features page.
- Importing from other form plugins.
- The Email action.
- The previous release, Core Forms 4.2.
Final Remarks
If you are on 4.3.8, polls are a proper feature rather than a leftover from 3.x, and the scripts that make a form smart are actually reaching your visitors. The idea to keep is that the patches in this line were about the plugin arriving intact, which is the part a release post usually skips.
After the update, I would do three things. Open one multi-step or inline-validated form on the front end and use it, because on 4.3.0 through 4.3.7 that script was not in the package. If the site sits behind a proxy, check the poll IP setting, or every voter looks like one address. And when importing, keep the source plugin installed until the new form’s submissions look right in the inbox.
The importing guide walks through each source, and the support page is the place to report an import that came across wrong.
That is all for 4.3. I hope the polls get some use.