D
P
0
← All articles Baca dalam Bahasa Indonesia

Shopify, Liquid & the Theme CLI

Judge.me Review Widget Renders Nothing on the PDP? `data-widget='widget'` Should Be `data-widget='review'`

· · 4 min read
Judge.me Review Widget Renders Nothing on the PDP? `data-widget='widget'` Should Be `data-widget='review'`

On a Shopify client site, I was asked to drop a Judge.me reviews block onto the product page (PDP). Judge.me publishes public docs for its Review Widget embed, so I figured this was a ten-minute job: hand-roll the widget div in a Liquid section, fill in the data attributes the docs list, paste the hydration script, done. I built the embed by hand: a <div> carrying data-widget='widget', then fed it review data through window.judgeme_review_widget_data, exactly as I read it.

The result: nothing. The div was in the DOM, I could see it in the inspector, but it was completely empty. No stars, no review list, no write-a-review button. The widget just sat there. And the maddening part: no error in the console. No 404 on a script, no red JavaScript exception. The element sat inert as if no part of Judge.me's code even knew it was supposed to own it.

Chasing the wrong thing

My first instinct: the data must be wrong. I checked window.judgeme_review_widget_data, it was populated. I checked the product ID I passed, correct. I diffed my div's attributes line by line against the docs, all matched. I suspected the Judge.me loader was running before my div existed, so I fiddled with load order and defer. Still empty.

Stuck, I reached for a control. I opened the Judge.me onboarding wizard in the admin and copied its Step 2 install code verbatim into the same section. The exact same widget rendered instantly. Stars, reviews, all of it, in one refresh. Two embeds, same product, same data, but one was dead and one was alive. The difference lived in the markup the wizard generated, not in my data.

The root cause

I put the two blocks of markup side by side, and the delta was not cosmetic. My hand-rolled embed was wrong in several implementation details that were nowhere in the public docs I had followed:

Here is what made it so misleading: the Judge.me JS bundle carries a legacy fallback gated on widget.size > 20. On some shops, old-style embeds appear to work, which is exactly what makes stale docs and tutorials look correct. In reality, these attributes and namespaces change between widget versions, and the onboarding wizard generates code that matches the exact JS bundle installed on that shop. The public docs I followed described a different version than the one the client's shop was actually running.

So this was not a bug in my data. The data was right. What was wrong was the markup contract: I had guessed attribute names and namespaces from general documentation, when the thing that binds it all together is version-specific detail that only the wizard knows.

The fix

The fix was almost anticlimactic: stop hand-rolling it. I pasted the wizard's install code verbatim, comments and all, without tidying up attribute names or namespaces. The only thing I added was a Liquid template-context conditional so the block only appears where it should.

<div id='judgeme_product_reviews' class='jdgm-widget jdgm-review-widget'
  data-product-title='{{ product.title | escape }}'
  data-id='{{ product.id }}'
  data-product-id='{{ product.id }}'
  data-widget='review'
  data-auto-install='false'
  data-shop-reviews-count='{{ shop.metafields.judgeme.shop_reviews_count | default: 0 | escape }}'
  data-entry-point='review_widget.js'
  data-entry-key='review-widget/main.js'>
</div>
{% if product.metafields.judgeme.review_widget_data %}
<script>jdgm.data ||= {}; jdgm.data.reviewWidget ||= {}; jdgm.data.reviewWidget[{{ product.id }}] = {{ product.metafields.judgeme.review_widget_data }}</script>
{% endif %}

Notice the hydration: review data is injected into jdgm.data.reviewWidget[product.id], not into some global variable I made up. And data-entry-point plus data-entry-key, which I had assumed were decorative, turned out to be exactly what makes the JS do its job.

The end result: a section that had ballooned to 414 lines of manual overrides and guesswork shrank to 75 lines using the official embed. The widget rendered on first load, and more importantly, it will not break silently the next time Judge.me ships a new widget version, because I am no longer pinning myself to one version's attribute names.

Takeaways