cancel
Showing results for 
Search instead for 
Did you mean: 
CommunityJedi22
Community Manager
Community Manager

This article is an advanced section for Enhanced SEO: Option 2. You can return to the main Option 2 documentation here.

Implementing the on_change Callback (Advanced Option 2)

Level of Effort: Medium-High

The on_change callback is an advanced feature that allows you to add detailed Review schema to your product pages. While the on_render The callback provides the data for an AggregateRating, the on_change The callback provides the full details of individual reviews as they are displayed on the page.

This is the ideal solution if you want your structured data to update dynamically as users filter, sort, or paginate through reviews.

Important: The on_change callback is a direct replacement for the on_render function. You must use one or the other in your pwr.render() call; you cannot use both on the same page.

How to Implement:

Add the on_change function to your pwr.render() configuration. The function will trigger whenever the list of reviews changes, allowing you to build and attach the Review schema objects to your existing Product schema.

Please note the following critical points:

  • Code Requires Modification: The example below is a template and will need to be modified for your specific site structure and needs.
  • Schema Structure Assumption: This code assumes your Product schema script tag (identified here as productschema) contains an array of product objects. If your schema is a single product object (which is more common), you must remove the for loops that iterate through scriptTagObject and instead attach the reviews directly to the single object (e.g., scriptTagObject.review = reviews;).
  • Element ID: The example uses getElementById('productschema') to find your product schema. You must ensure your schema script tag has this ID or update the selector to match your site.

Example:

<script>
pwr("render", {
    api_key: "YOUR_API_KEY",                          // Provided by PowerReviews
    locale: "en_US",                                  // Provided by PowerReviews
    merchant_group_id: "YOUR_MERCHANT_GROUP_ID",      // Provided by PowerReviews
    merchant_id: "YOUR_MERCHANT_ID",                  // Provided by PowerReviews
    page_id: "YOUR_PAGE_ID",
    review_wrapper_url: "YOUR_REVIEW_WRAPPER_URL",
    on_change: function(config, data) {
        // Only trigger when the ReviewList component updates
        if (config.component === 'ReviewList') {

            // First, find the existing product schema on the page
            var scriptTag = document.getElementById('productschema');
            if (!scriptTag) {
                console.log("PowerReviews SEO: Product schema tag not found.");
                return; // Exit if the schema tag doesn't exist
            }
            var scriptTagObject = JSON.parse(scriptTag.innerHTML);

            // CASE 1: Reviews are present, so build the schema
            if (data.review_count > 0 && data.reviews && data.reviews.length > 0) {
                var reviews = [];

                // Loop through each review provided by the callback
                for (var i = 0; i < data.reviews.length; i++) {
                    var pwrReview = data.reviews[i];
                    var review = {
                        "@type": "Review",
                        "name": pwrReview.details.headline,
                        "reviewBody": pwrReview.details.comments,
                        "datePublished": new Date(pwrReview.details.created_date).toISOString(),
                        "author": {
                            "@type": "Person",
                            "name": pwrReview.details.nickname
                        },
                        "reviewRating": {
                            "@type": "Rating",
                            "ratingValue": pwrReview.metrics.rating
                        }
                    };

                    // Optionally add location if it exists and is not undisclosed
                    if (pwrReview.details.location && (pwrReview.details.location).toLowerCase() != 'undisclosed') {
                        review["locationCreated"] = {
                            "@type": "AdministrativeArea",
                            "name": pwrReview.details.location
                        };
                    }
                    reviews.push(review);
                }

                // Attach the array of reviews to the Product schema
                // WARNING: This code assumes your schema is an ARRAY of products.
                // If it's a single object, change this loop to: scriptTagObject.review = reviews;
                for (var j = 0; j < scriptTagObject.length; j++) {
                    if (scriptTagObject[j]) {
                        scriptTagObject[j]['review'] = reviews;
                    }
                }

            // CASE 2: No reviews are visible (due to filtering/pagination)
            } else { 
                // Remove the review property from the schema to keep it clean
                // WARNING: This code assumes your schema is an ARRAY of products.
                // If it's a single object, change this loop to: delete scriptTagObject.review;
                for (var k = 0; k < scriptTagObject.length; k++) {
                    if (scriptTagObject[k] && scriptTagObject[k].review) {
                        delete scriptTagObject[k]['review'];
                    }
                }
            }
            
            // Finally, update the schema tag on the page with the new content
            scriptTag.innerHTML = JSON.stringify(scriptTagObject);
        }
    },
    components: {
        ReviewSnippet: 'pr-reviewsnippet',
        ReviewDisplay: 'pr-reviewdisplay'
    }
});
</script>

 

Version history
Revision #:
2 of 2
Last update:
‎12-08-2025 09:00 AM
Updated by:
 
Contributors