While running through some practice exercises anticipating one of my customer’s needs I had an issue with a clean, easy way to add icons to variables stored in ACF Pro. After much searching, I found no quick way to achieve it so I give you Custom Icon List Widget… May it one day have a snappier name
The Problem
Imagine you have a few rental properties and you are looking to list them, you create a website and a template and all seems lovely, now you come to list the facilities. using ACF we create the checkboxes for our rental property as below.
Now when you come to display this on the front end your options are fairly limited in how that is presented and you have to use things like ACF Field, ACF Repeater but they do not offer the ability to replace with icons as easily as this custom widget will. Below is an example of the output from ACF Field
Solution
My proposed solution is:
Create a non-invasive elementor widget using the Code Snippets plugin, importing ACF Data and running that through an algorithm that, if set, matches the text with an icon and, if not set, matches the text with a default icon.
Requirements
I will assume that you know what you are doing with the following plugins which are required for this process:
Code Snippets – This is needed for adding the PHP code from the admin panel, this can be done using an alternate method with FTP and making your own plugin but Code Snippets makes it faster and easier for you.
ACF Pro – goes without saying you need ACF Pro for the custom fields
Elementor Pro – I’m not spoon-feeding you!
Steps
- Create ACF field and attach it to your desired post (not in guide)
- Install, Active and select Code Snippets in the Admin Panel
- Click Add New
- Enter a Title such as “Checkbox to Icon List”
- Paste in the code below
- Change line
“$acf_values = get_field( ‘facilities2’ );”
to
$acf_values = get_field( ‘yourACFvariablename’ ); - Click “save changes” in the top right
- Click “activate” in the top right
- Now edit a page(I suggest using a template)
- Add the CSS to custom CSS on the page(guide below)
- Place in your widget
The Code
start_controls_section(
'content_section',
[
'label' => __( 'Variables', 'your-text-domain' ),
'tab' => \Elementor\Controls_Manager::TAB_CONTENT,
]
);
// Define the list items control
$this->add_control(
'list_items',
[
'label' => __( 'List Items', 'your-text-domain' ),
'type' => \Elementor\Controls_Manager::REPEATER,
'fields' => [
[
'name' => 'item_title',
'label' => __( 'Search String', 'your-text-domain' ),
'type' => \Elementor\Controls_Manager::TEXT,
'default' => esc_html__( 'List Title' , 'textdomain' ),
],
[
'name' => 'icon',
'label' => __( 'Replace Icon', 'your-text-domain' ),
'type' => \Elementor\Controls_Manager::ICONS,
'default' => [
'value' => 'fas fa-star',
'library' => 'fa-solid',
],
],
],
'title_field' => '{{{item_title}}}',
]
);
// Add a default icon control
$this->add_control(
'default_icon',
[
'label' => __( 'Default Icon', 'your-text-domain' ),
'type' => \Elementor\Controls_Manager::ICONS,
'default' => [
'value' => 'fas fa-star',
'library' => 'fa-solid',
],
]
);
$this->end_controls_section();
}
protected function render() {
if ( ! function_exists( 'get_field' ) ) {
return;
}
$settings = $this->get_settings_for_display();
$acf_values = get_field( 'facilities2' );
if ( is_array( $acf_values ) && !empty( $acf_values ) && is_array( $settings['list_items'] ) && !empty( $settings['list_items'] ) ) {
// Convert list items into an associative array for easier lookup
$list_items_assoc = [];
foreach ( $settings['list_items'] as $item ) {
$list_items_assoc[$item['item_title']] = $item['icon'];
}
// Render the updated list
echo ' ';
foreach ( $acf_values as $acf_value ) {
if ( isset( $list_items_assoc[$acf_value] ) ) {
$icon = $list_items_assoc[$acf_value];
} else {
$icon = $settings['default_icon']; // Use the default icon if no matching item is found
}
ob_start();
\Elementor\Icons_Manager::render_icon( $icon, [ 'aria-hidden' => 'true' ] );
$icon_html = ob_get_clean();
?>
-
';
}
}
}
$widgets_manager->register_widget_type( new Custom_Icon_List_Widget() );
} );
?>
The Code (Pt.2)
because of how HTML UL work you need to use CSS to remove bulletpoints and lineup icons with text. In elementor edit your template or page > settings > advanced > custom CSS >
.custom-icon-list {
list-style-type: none; /* Removes bullet points */
padding: 0; /* Removes default padding */
margin: 0; /* Removes default margin */
}
.custom-icon-list li {
margin-bottom: 10px; /* Adds space below each list item */
display: flex; /* Aligns icon and text horizontally */
align-items: center; /* Vertically centers icon and text */
flex-wrap: nowrap; /* Prevents wrapping within list items */
}
.custom-icon-list .icon-wrapper {
margin-right: 10px; /* Adds space between icon and text */
display: inline-block; /* Ensures icon wrapper behaves as an inline element */
}
What it all does
This is the primary visual controls you see inside elementor, We start by creating the sections and titles, then add the list of items and the defaults associated with them.
protected function _register_controls() {
$this->start_controls_section(
'content_section',
[
'label' => __( 'Variables', 'your-text-domain' ),
'tab' => \Elementor\Controls_Manager::TAB_CONTENT,
]
);
// Define the list items control
$this->add_control(
'list_items',
[
'label' => __( 'List Items', 'your-text-domain' ),
'type' => \Elementor\Controls_Manager::REPEATER,
'fields' => [
[
'name' => 'item_title',
'label' => __( 'Search String', 'your-text-domain' ),
'type' => \Elementor\Controls_Manager::TEXT,
'default' => esc_html__( 'List Title' , 'textdomain' ),
],
[
'name' => 'icon',
'label' => __( 'Replace Icon', 'your-text-domain' ),
'type' => \Elementor\Controls_Manager::ICONS,
'default' => [
'value' => 'fas fa-star',
'library' => 'fa-solid',
],
],
],
'title_field' => '{{{item_title}}}',
]
);
// Add a default icon control
$this->add_control(
'default_icon',
[
'label' => __( 'Default Icon', 'your-text-domain' ),
'type' => \Elementor\Controls_Manager::ICONS,
'default' => [
'value' => 'fas fa-star',
'library' => 'fa-solid',
],
]
);
$this->end_controls_section();
}
The Rendering is where the heavy lifting is done. This function renders the UX and loops through the data, and applies the algorithm.
protected function render() {
if ( ! function_exists( 'get_field' ) ) {
return;
}
$settings = $this->get_settings_for_display();
$acf_values = get_field( 'facilities2' );
if ( is_array( $acf_values ) && !empty( $acf_values ) && is_array( $settings['list_items'] ) && !empty( $settings['list_items'] ) ) {
// Convert list items into an associative array for easier lookup
$list_items_assoc = [];
foreach ( $settings['list_items'] as $item ) {
$list_items_assoc[$item['item_title']] = $item['icon'];
}
// Render the updated list
echo ' ';
foreach ( $acf_values as $acf_value ) {
if ( isset( $list_items_assoc[$acf_value] ) ) {
$icon = $list_items_assoc[$acf_value];
} else {
$icon = $settings['default_icon']; // Use the default icon if no matching item is found
}
ob_start();
\Elementor\Icons_Manager::render_icon( $icon, [ 'aria-hidden' => 'true' ] );
$icon_html = ob_get_clean();
?>
-
';
}
}
Lets go through in a bit more detail
$settings = $this->get_settings_for_display();
$acf_values = get_field( 'facilities2' );
This pulls the ACF Field from the SQL database
if ( is_array( $acf_values ) && !empty( $acf_values ) && is_array( $settings['list_items'] ) && !empty( $settings['list_items'] ) ) {
Next we make sure that our validations are correct (data is arrays for loops and is not NULL)
$list_items_assoc = [];
foreach ( $settings['list_items'] as $item ) {
$list_items_assoc[$item['item_title']] = $item['icon'];
}
Prepare our data into an array ready for usage
echo ' ';
foreach ( $acf_values as $acf_value ) {
if ( isset( $list_items_assoc[$acf_value] ) ) {
$icon = $list_items_assoc[$acf_value];
} else {
$icon = $settings['default_icon']; // Use the default icon if no matching item is found
}
ob_start();
\Elementor\Icons_Manager::render_icon( $icon, [ 'aria-hidden' => 'true' ] );
$icon_html = ob_get_clean();
?>
-
';
}
Now output some HTML, it creates an Unordered list, then checks if a word has an associated Icon, if it does have an Icon it displays it, if it does not have an icon it uses a default icon set in the UI.
Because $icon_html is already rendered as an output it must be created as an object and cleaned (line 8-10) otherwise you will get a 1 next to your array output.
Next on my agenda will be adding the ability to enter which ACF field you select from and looking at sorting these into columns or a table to help with display.


