If you’re running a WooCommerce store and want to connect your products to Facebook Catalogue or Google Merchant Center, you usually rely on a plugin. However, plugins can add bloat, slow down your website, or offer limited control.
This guide shows you how to generate a dynamic XML product feed from WooCommerce without installing any plugin. The feed is fully compatible with Facebook Commerce Manager and Google Shopping, supports both simple and variable products, and includes custom attributes like size, colour, and more.Why Build Your Own Feed?
Why build your own feed?
- No need for extra plugins
- Full control over structure and attributes
- Supports all WooCommerce product types
- Dynamically includes all variation attributes
- Can be used for Facebook, Google, or any XML-based product platform
Step 1: Create the XML feed file
Create a new file in your website root directory name it as product-feed-xml.php. Paste the following code inside it.
<?php
require_once('wp-load.php');
ob_clean(); // Clean any prior output to prevent XML errors
header('Content-Type: application/xml; charset=utf-8');
echo '<?xml version="1.0" encoding="UTF-8"?>';
?>
<rss version="2.0" xmlns:g="http://base.google.com/ns/1.0">
<channel>
<title>Your Store Feed</title>
<link><?php echo esc_url(site_url()); ?></link>
<description>WooCommerce Product Feed</description>
<?php
$args = [
'post_type' => ['product', 'product_variation'],
'post_status' => 'publish',
'posts_per_page' => -1
];
$loop = new WP_Query($args);
if ($loop->have_posts()) {
while ($loop->have_posts()) {
$loop->the_post();
$product = wc_get_product(get_the_ID());
if (!$product || !$product->is_visible()) continue;
$is_variation = $product->is_type('variation');
$parent_id = $is_variation ? $product->get_parent_id() : $product->get_id();
$parent = wc_get_product($parent_id);
// Prices
$currency = get_woocommerce_currency();
$regular_price = $product->get_regular_price();
$sale_price = $product->get_sale_price();
$price = $regular_price ?: $sale_price;
if (!$price) continue; // skip product with no price
// Other fields
$id = $product->get_id();
$title = $product->get_name();
$description = strip_tags($parent->get_short_description() ?: $parent->get_description());
$availability = $product->is_in_stock() ? 'in stock' : 'out of stock';
$condition = 'new';
$link = get_permalink($parent_id);
$image = wp_get_attachment_url($product->get_image_id() ?: $parent->get_image_id());
$category_list = wp_get_post_terms($parent_id, 'product_cat', ['fields' => 'names']);
$product_type = implode(" > ", $category_list);
$item_group_id = $is_variation ? $parent_id : '';
$brand = 'Your Brand'; // Customize if needed
?>
<item>
<g:id><?php echo $id; ?></g:id>
<title><![CDATA[<?php echo $title; ?>]]></title>
<description><![CDATA[<?php echo $description; ?>]]></description>
<link><?php echo esc_url($link); ?></link>
<g:image_link><?php echo esc_url($image); ?></g:image_link>
<g:availability><?php echo $availability; ?></g:availability>
<g:condition><?php echo $condition; ?></g:condition>
<g:price><?php echo number_format($price, 2); ?> <?php echo $currency; ?></g:price>
<?php if ($sale_price): ?>
<g:sale_price><?php echo number_format($sale_price, 2); ?> <?php echo $currency; ?></g:sale_price>
<?php endif; ?>
<g:brand><?php echo esc_html($brand); ?></g:brand>
<g:product_type><![CDATA[<?php echo $product_type; ?>]]></g:product_type>
<?php if ($item_group_id): ?>
<g:item_group_id><?php echo $item_group_id; ?></g:item_group_id>
<?php endif; ?>
<?php
// Output all variation attributes dynamically
$attributes = $product->get_attributes();
foreach ($attributes as $attribute_name => $attribute) {
$label = wc_attribute_label($attribute_name);
$value = $product->get_attribute($attribute_name);
if (!empty($value)) {
$tag_name = strtolower(preg_replace('/[^a-z0-9_]/i', '_', $label));
echo " <g:" . esc_html($tag_name) . ">" . esc_html($value) . "</g:" . esc_html($tag_name) . ">\n";
}
}
?>
</item>
<?php
}
}
wp_reset_postdata();
?>
</channel>
</rss>
Step 2: Access and test the feed
Visit: https://yourdomain.com/product-feed-xml.php
You’ll see a fully structured XML file that can be submitted to:
You can schedule Facebook or Google to fetch your feed daily using the public feed URL: https://yourdomain.com/product-feed-xml.php and you do not need to manually upload a CSV or XML file.