TechEarl

How to Populate ACF Fields from Gravity Forms

Gravity Forms can create WordPress posts on submission, but mapping form fields to ACF fields requires a hook. The canonical pattern uses gform_after_submission to call update_field. Plus the dedicated Gravity Forms + ACF plugin path.

Ishan Karunaratne⏱️ 5 min readUpdated
Share thisCopied
Map Gravity Forms field submissions to ACF fields. gform_after_submission hook pattern, dedicated GF+ACF plugin, field-mapping conventions.

Gravity Forms can create WordPress posts on submission via the Post Fields module, but Post Fields only map to standard WordPress post fields (title, content, excerpt, custom taxonomies) and not to ACF fields. To populate ACF fields from a Gravity Forms submission, the canonical pattern is the gform_after_submission hook plus update_field calls. The dedicated Gravity Forms + ACF add-on plugin is the alternative when you want a UI-driven mapping. Here is each.

Jump to:

Why Post Fields alone do not cover ACF

Gravity Forms has a built-in "Post Fields" group that maps form inputs directly to standard WordPress post fields:

  • Title
  • Body (post_content)
  • Excerpt
  • Tags
  • Category
  • Custom Field (writes to wp_postmeta via update_post_meta)

The Custom Field option does write to wp_postmeta, which is where ACF data lives, BUT it writes only the raw value and not the ACF field-key reference row. The next time ACF reads the field, it may not recognize the value as ACF-managed and may behave unexpectedly (the editor UI shows the value but update_field calls behave oddly).

Using update_field after the form submission is the canonical correct path. The hook is gform_after_submission.

The gform_after_submission hook pattern

gform_after_submission fires after Gravity Forms creates the entry and the post (if Post Fields are configured). The callback receives the entry and the form:

php
add_action( 'gform_after_submission_5', 'populate_acf_from_form', 10, 2 );
// "_5" targets form ID 5; use 'gform_after_submission' to apply to all forms

function populate_acf_from_form( $entry, $form ) {
    // Get the post ID created by the form (if Post Fields are used)
    $post_id = rgar( $entry, 'post_id' );

    if ( ! $post_id ) {
        // No post was created by Post Fields; nothing to update
        return;
    }

    // Map form field IDs to ACF field names
    update_field( 'phone',         rgar( $entry, '3' ), $post_id ); // form field ID 3
    update_field( 'company',       rgar( $entry, '4' ), $post_id );
    update_field( 'industry',      rgar( $entry, '5' ), $post_id );
    update_field( 'is_subscriber', rgar( $entry, '6' ) === 'yes', $post_id );
}

rgar() is Gravity Forms' helper for "read get array": it safely reads from the entry array and returns empty string if the key does not exist.

The form field IDs (3, 4, 5, 6 in the example) are visible in the Gravity Forms form editor next to each field. Use these exact IDs in the mapping.

Mapping form fields to ACF fields

For maintainability, define the mapping in a configuration array rather than inline:

php
add_action( 'gform_after_submission_5', function ( $entry, $form ) {
    $post_id = rgar( $entry, 'post_id' );
    if ( ! $post_id ) return;

    $mapping = [
        // form_field_id => acf_field_name
        '3' => 'phone',
        '4' => 'company',
        '5' => 'industry',
        '6' => 'is_subscriber',
        '7' => 'website_url',
        '8' => 'years_in_business',
    ];

    foreach ( $mapping as $form_field_id => $acf_field_name ) {
        $value = rgar( $entry, $form_field_id );
        update_field( $acf_field_name, $value, $post_id );
    }
}, 10, 2 );

When the form changes (new field added, existing field renumbered), update the mapping array. The rest of the logic does not need to change.

Handling complex ACF field types from a form

Date fields: Gravity Forms returns the date string in the form's configured format. ACF expects Ymd for storage. Convert:

php
$date_string = rgar( $entry, '9' ); // e.g., "12/25/2026"
$date_obj = DateTime::createFromFormat( 'm/d/Y', $date_string );
if ( $date_obj ) {
    update_field( 'event_date', $date_obj->format( 'Ymd' ), $post_id );
}

Checkbox fields: Gravity Forms returns checkbox values as multiple entries with sub-keys (6.1, 6.2, 6.3 for the three checkbox options). Collect them into an array:

php
$checkboxes = [];
foreach ( [ '6.1', '6.2', '6.3' ] as $sub_key ) {
    $val = rgar( $entry, $sub_key );
    if ( ! empty( $val ) ) $checkboxes[] = $val;
}
update_field( 'tags', $checkboxes, $post_id );

File upload fields: Gravity Forms returns the uploaded file URL. ACF Image fields expect an attachment ID. Convert via attachment_url_to_postid or by sideloading the file:

php
$file_url = rgar( $entry, '10' );
if ( $file_url ) {
    $attachment_id = attachment_url_to_postid( $file_url );
    if ( $attachment_id ) {
        update_field( 'featured_image', $attachment_id, $post_id );
    }
}

If the GF upload writes the file outside the media library, you need to sideload it first with media_sideload_image or media_handle_sideload and then use the resulting attachment ID.

Repeater fields: Gravity Forms does not natively support Repeater-shaped submissions. For multi-row data, either submit individual rows via separate form submissions or use a different form pattern. Or look at Sending Gravity Forms Data Into ACF Repeater Fields.

The dedicated Gravity Forms + ACF plugin path

For sites where the form-to-ACF mapping changes frequently and you want a UI-driven approach, the dedicated Gravity Forms + ACF add-on plugins handle the mapping via the Gravity Forms admin. The most-used one is "ACF Frontend Form" (third-party); the official Gravity Forms add-ons handle some ACF field types but coverage varies.

The trade-off:

  • Hook-based mapping (above): Code-managed, version-controlled, explicit, supports any ACF field type.
  • Plugin-based mapping: UI-managed, easier for non-developers, may not cover every ACF field type.

For agency work I prefer the hook pattern because it lives in code and travels with deploys. For sites where the marketing team needs to add new fields without a developer, the plugin path makes sense.

Validation before save

gform_after_submission fires after Gravity Forms has validated the form and saved the entry. If you need to abort the post update based on a condition the form does not check, do it in the callback:

php
add_action( 'gform_after_submission_5', function ( $entry, $form ) {
    $post_id = rgar( $entry, 'post_id' );
    if ( ! $post_id ) return;

    // Custom validation: company field must contain a known whitelist
    $allowed = [ 'Acme', 'Globex', 'Initech' ];
    $company = rgar( $entry, '4' );
    if ( ! in_array( $company, $allowed, true ) ) {
        // Mark post as pending review instead of populating ACF
        wp_update_post( [ 'ID' => $post_id, 'post_status' => 'pending' ] );
        return;
    }

    // Normal mapping
    update_field( 'company', $company, $post_id );
}, 10, 2 );

For pre-save validation (preventing the form from submitting at all), use gform_validation instead.

User permissions and post status

Gravity Forms creates posts as the currently-logged-in user (or as a configured user if the user is not logged in). For public-facing forms, posts often get created with post_status = 'pending' so an editor can review before publish.

If your ACF population should only run for published posts:

php
add_action( 'gform_after_submission_5', function ( $entry, $form ) {
    $post_id = rgar( $entry, 'post_id' );
    if ( ! $post_id || get_post_status( $post_id ) !== 'publish' ) return;

    // ACF mapping only for published posts
}, 10, 2 );

Or, more commonly, populate ACF regardless of status (so the data is there when the editor reviews and publishes).

For the broader pattern of Gravity Forms + ACF integration including Repeater data, see Sending Gravity Forms Data Into ACF Repeater Fields. For the agency operations side (lead-capture forms, client onboarding forms), see How to Start a WordPress Agency in 2026 Without Burning Yourself Out.

Sources

Authoritative references this article was fact-checked against.

TagsWordPressACFGravity FormsForms

Found this useful? Pass it on.

Copied

Ishan Karunaratne

Tech Architect · Software Engineer · AI/DevOps

Tech architect and software engineer with 20+ years building software, Linux systems, and DevOps infrastructure, and lately working AI into the stack. Currently Chief Technology Officer at a healthcare tech startup, which is where most of these field notes come from.

Keep reading

Related posts

How to Update ACF Fields Programmatically

update_field() is the canonical way to write ACF data programmatically: the function signature, how to write to Repeater and Flexible Content fields, when to use field keys instead of names, options pages and user meta, and the gotchas that bite.

How to Import a CSV File Into MySQL

Import a CSV into MySQL using LOAD DATA INFILE, LOAD DATA LOCAL INFILE, or the mysqlimport command. Covers header rows, encoding, the FILE privilege, and broken CSVs.