Injecting data into Elementor Templates

I was working on a project recently where the team wanted to use Elementor to create “post skins”, i.e. they wanted to reference other posts within a page and use Elementor to create display templates for those posts. Elementor has a loop builder but it’s pretty limited in terms of custom queries, display logic, and custom fields. The technique I came up with ended up being pretty powerful and useful for other projects as well.

The basic idea

Simply set the context using data from a post and render an Elementor template with that data. This would be useful for an author box for example.

PHP
// Get the post and set the context.
global $post;
$post = get_field('some_post_field'); // Get a post ID however you want.
setup_postdata($post);

// Render an Elementor template, which will use data from the post you set above.
echo do_shortcode('[elementor-template id="123"]');

wp_reset_postdata();

You can also loop through several posts and use Elementor to create a template which is used for each post. Useful for search results, directories, related products, etc.

PHP
// Get the posts you want.
$args = [
  // Whatever WP_Query args you need.
];
$the_query = new WP_Query( $args );

// Render an Elementor template for each fetched post.
if ($the_query->have_posts()) {
  while ($the_query->have_posts()) {
    $the_query->the_post();
  
    echo do_shortcode('[elementor-template id="123"]');
  }
}

wp_reset_postdata();

The functions above can be used in a page template or wrapped in a shortcode and dropped anywhere, even in other Elementor templates!

Conditional views

You can display different templates depending on certain values in the post data. For example, if you have a “media” post type which can be video or audio, you could use a different template depending on media type:

PHP
$args = [
  // Args args args.
];
$the_query = new WP_Query( $args );

if ($the_query->have_posts()) {
  while ($the_query->have_posts()) {
    $the_query->the_post();

    $type = get_field('media_type'); // ACF field.

    if ($type === 'video') {
      echo do_shortcode('[elementor-template id="123"]');
    } elseif ($type === 'audio') {
      echo do_shortcode('[elementor-template id="234"]');
    }
  }
}

wp_reset_postdata();

Getting crazy

This particular project had a fairly odd but interesting requirement. We needed to display data from a post but also have the ability to override or have fallbacks for certain data. We were working with a custom post type for Resources, and each page on the site had a custom field for selecting related Resources. Resources had custom fields for overriding the post title and URL. There was also a custom field for an excerpt (long story), and we needed a fallback to a custom-length excerpt.

Here’s what that looked like:

PHP
// A custom field on the page allows you to select a number of related Resources.
if( have_rows('related_resources') ):
  while( have_rows('related_resources') ) : the_row();

    global $post;
    $post = get_sub_field('rgi_resource');
    setup_postdata( $post );

    // These global variables are used in shortcodes in the Elementor post skin.
    $GLOBALS['rgi_title'] = get_sub_field('rgi_title') ? : $post->post_title; // If there's no override for the title, use the post title.
    $GLOBALS['rgi_url'] = get_sub_field('rgi_url') ? : (get_field('custom_url', $post->ID) ? : get_permalink($post->ID)); // Two fallbacks here. First, check if the Resource URL was set on the page where the resources are displayed. Then check if the URL was set in the Resource post. Then fall back to the Resource permalink.
    $GLOBALS['rgi_excerpt'] = get_sub_field('rgi_excerpt') ? : do_shortcode( '[excerpt words="25"]' ); // If the excerpt is not set, use the first 25 words of the Resource.

    // Render an Elementor template (which includes shortcodes that reference the global vars set above).
    echo do_shortcode('[elementor-template id="2029"]');
    wp_reset_postdata();

  endwhile;
endif;

// These shortcodes simply output global variables. They are used for setting field values in an Elementor template, and allow for complex logic as shown above.
add_shortcode('rgi_title', 'ta_rgi_title');
function ta_rgi_title() {
  return $GLOBALS['rgi_title'];
}
add_shortcode('rgi_url', 'ta_rgi_url');
function ta_rgi_url() {
  return $GLOBALS['rgi_url'];
}
add_shortcode('rgi_excerpt', 'ta_rgi_excerpt');
function ta_rgi_excerpt() {
  return $GLOBALS['rgi_excerpt'];
}

Block editor

To take things to the next level, this technique can be combined with the WordPress block editor, which I ended up doing on a different project. With this approach, the block editor is used for content, Elementor is used for design, and complex logic stays in the code. There are several benefits:

  • Easier editing experience for content editors. The block editor focuses on content, not design.
  • Designers are happy because they can edit the design visually in Elementor.
  • Overall system complexity is reduced because logic stays in the code instead of implementing it through a visual builder, which is usually hacky and awkward at best.