If you’ve been working with WordPress and Advanced Custom Fields for some time, you’ll know that ACF is not just a tool for adding custom fields in the backend: it also allows you to create custom post types and custom taxonomies.
However, few people know that ACF also allows you to create fully functional forms on the website’s front-end, to add content to the CPT you’ve programmed and with the same custom fields you’ve created in the back-end, without the user having to access the admin panel.
Some time ago, we had to create exactly this system for a client, and the official ACF documentation seemed a bit sparse and not very detailed, so we decided to publish this tutorial for other developers who need to do the same.
Important note: This tutorial is intended for developers already familiar with WordPress, Custom Post Types (CPT), and the basic operation of ACF. We will not explain how to install the plugin and create the necessary fields or how to create a CPT from scratch; we assume you already understand that.
What we will see, step by step, is:
- How to set up a front-end submission form that saves entries as drafts
- WordPress notifies you by email every time someone submits an entry
- Optionally, the user is redirected to a “Submission Received” confirmation landing page or similar
The Scenario
Imagine you manage DirectorioEmpresasACME.com, an online directory where companies can be listed with all their information.
Until now, you added the company profiles manually from the WordPress admin, but you want the companies themselves to be able to submit their information via a public form, and you simply review and publish each submission.
For this, you have created in WordPress:
- A Custom Post Type called “Companies”, whose internal slug is /company/
- An ACF field group associated with that Custom Post Type, with the following fields:
| Data | Field Name | Type |
|---|---|---|
| Legal Name | company_legal_name | Text |
| Sector | company_sector | Selection |
| Number of Employees | number_of_employees | Selection |
| Website | company_website | URL |
| Phone | company_phone | Text |
| Annual Revenue | company_annual_revenue | Selection |
| Province | company_province | Selection |
| City | company_city | Text |
Additionally:
- The post title field (native to WordPress) will be used as the common company name
- The post content field (also native to WordPress) will allow pasting a company description
What you need to know before you start
ACF offers two functions to build front-end forms:
acf_form_head()> Se coloca antes deget_header()- This function registers the necessary assets (CSS and JS), processes submitted data, and manages redirection
- It is mandatory and must be executed in that order
acf_form()> Se coloca dentro de la plantilla, donde quieras que aparezca el formulario- As you will see in the code snippet, it accepts an array of parameters to configure its behavior
The key to creating a new post (instead of editing an existing one) is the 'post_id' => 'new_post' parameter, combined with 'empresa' and 'draft' to specify the content type and post status.
For the form to work correctly, we need to create 2 things:
- A template within our theme where the ACF form will be loaded
- A function within the functions.php file that will send us an email notification every time someone submits a form
Steps to get your ACF form up and running
Let’s look at the necessary steps to get this working.
Step 1: Create the page template and the form page
Create the form-enviar-empresa.php file in your child theme’s folder. You can do this via FTP or through the server’s or hosting’s file browser.
Remember to use a child theme, or you will lose the file with the next update of your WordPress theme
This file will act as a WordPress page template and will contain all the form’s logic.
This is the code we will use for the template (the template name and its content are in Spanish):
<?php
/**
* Template Name: ACF Form Enviar Empresa
*/
// IMPORTANT: acf_form_head() must go before get_header()
acf_form_head();
get_header();
?>
<div id="formulario-empresa" style="max-width: 900px; margin: 60px auto; padding: 0 20px;">
<h1>Añade tu empresa al directorio</h1>
<p>Rellena el formulario con los datos de tu empresa. Revisaremos la información antes de publicarla en el directorio.</p>
<?php
acf_form(array(
'post_id' => 'new_post',
'post_title' => true,
'post_content' => true,
'new_post' => array(
'post_type' => 'empresa',
'post_status' => 'draft',
),
'submit_value' => 'Enviar empresa',
'updated_message' => '¡Gracias! Tu empresa ha sido recibida y la revisaremos pronto.',
));
?>
</div>
<?php get_footer(); ?>With this configuration, after the form submission, the user remains on the same page and is shown the confirmation message Thank you! Your company has been received and we will review it soon.
If you prefer to redirect the user to a specific landing page, see the alternative section below.
Step 2 — Create the page in WordPress
With the template file already uploaded to your child theme:
- Go to Pages > Add New
- Give it a descriptive title, like “Company Submission Form” or something similar
- Under Page Attributes, select the php template we created earlier, in our case
form-enviar-empresa.php - Add meta title and meta description using your chosen SEO plugin
- Publish the page
WordPress will use your PHP template instead of the visual editor, so there’s no need to add anything in the page editor.
Step 3: Create an email notification in functions.php
Every time someone submits the form, you can receive a notification to let you know that a new company has been added as a draft, pending your review and publication.
To achieve this, add the following to the functions.php of your child theme:
/**
* ACF Form email notification
*/
add_action('acf/save_post', 'notificar_nueva_empresa', 20);
function notificar_nueva_empresa( $post_id ) {
if ( get_post_type($post_id) !== 'empresa' ) {
return;
}
if ( is_admin() ) {
return;
}
if ( get_post_status($post_id) !== 'draft' ) {
return;
}
$nombre = get_the_title($post_id);
$sector = get_field('sector_empresa', $post_id);
$trabajadores = get_field('num_trabajadores', $post_id);
$web = get_field('web_empresa', $post_id);
$telefono = get_field('telefono_empresa', $post_id);
$provincia = get_field('provincia_empresa', $post_id);
$ciudad = get_field('ciudad_empresa', $post_id);
$enlace_admin = admin_url('post.php?post=' . $post_id . '&action=edit');
$to = 'info@directorioEmpresasACME.com';
$subject = '[DirectorioEmpresas] Nueva empresa pendiente: ' . $nombre;
$body = "Se ha recibido una nueva empresa para revisar.\n\n";
$body .= "──────────────────────────\n";
$body .= "Empresa: {$nombre}\n";
$body .= "Sector: {$sector}\n";
$body .= "Trabajadores: {$trabajadores}\n";
$body .= "Web: {$web}\n";
$body .= "Teléfono: {$telefono}\n";
$body .= "Provincia: {$provincia}\n";
$body .= "Ciudad: {$ciudad}\n";
$body .= "──────────────────────────\n\n";
$body .= "Revisar y publicar:\n{$enlace_admin}\n";
wp_mail($to, $subject, $body);
}Notes:
The acf/save_post hook is triggered every time ACF saves data, both from the admin and the front-end, so we would receive an email every time a draft post was created, even by ourselves from the WordPress dashboard.
Since it doesn’t make much sense to send emails constantly, the is_admin() and get_post_type() checks ensure that the email is only sent when appropriate.
Alternative: Redirect to a confirmation landing page after form submission
The previous php snippet only displays a message after form submission, such as Thank you! Your company has been received and we will review it soon. The user remains on the same page, and nothing else happens.
If you prefer, you can redirect the user to a specific URL after submission.
Create a landing page to direct the user to
Create a landing page to direct the user to once they have submitted the form; in our example, we have created this page:
https://DirectorioEmpresasACME.com/empresa-recibida/
On the landing page, inform the user that the form has been submitted successfully. You can also use the page to sell subscriptions or services related to your website. Be elegant and professional!
Modify your theme’s php template
Then, you must modify the template for your theme that you created previously, in our case form-enviar-empresa.php
You will simply need to replace the 'updated_message' parameter with 'return' in the acf_form() function and point to the landing page you previously created, like this:
acf_form(array(
'post_id' => 'new_post',
'post_title' => true,
'post_content' => true,
'new_post' => array(
'post_type' => 'empresa',
'post_status' => 'draft',
),
'submit_value' => 'Enviar empresa',
'return' => 'https://DirectorioEmpresasACME.com/empresa-recibida/',
));Notes and Troubleshooting
Additional information to ensure your ACF form works perfectly.
If the form appears empty
For acf_form() to display the fields, the ACF field group must be configured to show in the correct location.
If the form appears empty and no fields appear when creating a new company in the WordPress dashboard, you have likely assigned the field group to the wrong post type (by default, ACF assigns a new field group to ‘Posts’).
Go to ACF > Field Groups and edit your group. The location rule should display the field group in the correct place, in our hypothetical example: Post Type > is equal to > Companies
Fields are not responsive on mobile
The form fields inherit the visual styles you have configured in ACF.
That is, if you have configured 3 fields to display at 33% width in ACF > Presentation > Container Attributes, because you want to create 3 columns (or a row with 3 fields), this is how they will display in the form.
These widths are applied as inline styles in the HTML and may not respond automatically on mobile.
If this happens, to fix it, add this to the functions.php:
/**
* CSS responsive para el formulario de empresas
*/
add_action('wp_head', 'formulario_empresa_css_responsive');
function formulario_empresa_css_responsive() {
if ( ! is_page('añade-tu-empresa') ) {
return;
}
?>
<style>
@media (max-width: 768px) {
#formulario-empresa .acf-field[style] {
width: 100% !important;
float: none !important;
}
}
</style>
<?php
}The [style] selector specifically targets elements with inline styles, which are what ACF generates for field widths and would otherwise ignore a normal CSS rule.
The complete workflow
- User fills out the form on the page we created
- The post is created as a draft in the ‘company’ Custom Post Type section
- Option A: a confirmation message is shown to the user on the same page after successful form submission
- Option B: the user is redirected to another page, for example DirectorioEmpresasACME.com/company-received/
- You receive an email notification at info@DirectorioEmpresasACME.com with a summary of the submitted information and a direct link to the draft
- You log into your WordPress dashboard, review, and publish
Final Considerations
Some points to consider before putting this into production:
- The form works for everyone, including non-logged-in users
- The
acf_form()function works for everyone by default, without needing to be registered. - If you want to restrict form usage to logged-in users, add a check with
is_user_logged_in()beforeacf_form_head().
- The
- The conditional logic you have programmed in ACF will also work in the form.
- If you have fields with conditional logic configured in ACF (for example, showing the “city” field only if the “province” is whatever), that programmed logic is the same that will be loaded in the form, because ACF loads its own JavaScript to manage it.
- Clear the cache if styles do not load correctly (yes, it’s almost always a cache issue)
- If you use a cache plugin or a page builder with static CSS generation (like Divi), styles and scripts may not load correctly for non-logged-in users until the cache is completely cleared.
- If you see that the form works when logged in but not in incognito mode, that’s the first place to look.
- Clear plugin, server, and browser caches and recheck the styles.
- Note: We have not installed any anti-spam system.
- This form does not include any anti-spam system. If you put it into production and find that you are receiving spam submissions, consider adding a honeypot system or integrating Captcha using one of the plugins available in the WordPress repository
We hope this tutorial has been useful to you
If you have experience with ACF and CPT in WordPress, we hope this tutorial expands your knowledge of this powerful WordPress plugin. Best regards and see you soon!

