Platforms

Elementor Google reviews widget: what a hidden tab does to it

2 August 2026 · JustReview

Elementor accepts a review snippet in its HTML widget, and that part is uneventful. The catch is where the snippet lands. Drop it into a Tabs widget, a Toggle, an Accordion or a Popup Builder template, and the markup sits in a panel that is closed while your scripts execute. A reviews slider born in a closed panel comes out wrong in a specific, repeatable way: one review stretched across the entire block instead of four beside each other, and it stays like that for the whole visit.

Nothing here is an Elementor defect, and it is not exclusive to us either. It is simply what any component that must know its own width does when it is started before it has one. What follows is the arithmetic, the cause, and a four-line rewrite of your init call that removes the problem.

Two ways into an Elementor page

HTML widget (paste the snippet)Paid reviews addon
Where the reviews liveyour own review accountthe addon vendor’s service
What happens when you stop paying the addonnothing, it is your codethe widget goes away
Elementor version couplingnone, it is plain HTMLtied to the addon’s updates
Sources you can mixevery source you have connectedthe ones that addon supports
Layout controlthe widget’s own settingsthe addon’s controls, inside Elementor
Effortpaste onceinstall, then configure
Hidden containersyour problem, see belowalso your problem, quietly

An addon feels easier during the first afternoon and turns awkward the day its author walks away from it, which for review addons happens often. Pasting a snippet asks for one piece of knowledge instead, and that knowledge is rarely written down anywhere, so here it is.

What a closed panel does to a reviews slider

These runs are from 2 August 2026: a scripted Chrome instance, the Testimonials snippet exactly as the JustReview panel writes it, dropped into a 1140 px column area on a 1280 px screen. Identical markup in every run. The single variable is whether the panel holding that markup was open at the moment the init call executed.

ScenarioSlider width it measuredCard widthCards in view
Container visible at init (ordinary section)1060 px265 px4
Container display: none at init0 px0 px0
Same, 2 seconds after the tab is openedstill 0 px1060 px1
Same, 8 seconds after the tab is openedstill 0 px1060 px1
Fixed with a resize event after the reveal1060 px265 px4
Init deferred until the reveal1060 px265 px4

Two rows of that table deserve more attention than the rest.

The failure does not look like a failure. The block is still 857 px tall, the reviews are all there, the arrows work. It simply shows one review at a time where it should show four, so the page reads as if somebody chose that. This is why the problem survives launches: nobody files a bug about a section that renders, they just quietly live with a weaker one.

Waiting changes nothing. Eight seconds after the panel opened, the slider still believed it was zero pixels wide. Opening a panel is not an event any slider listens to, and ours does not sit there re-measuring on a timer, so the wrong numbers stay wrong until something forces a fresh calculation.

Why it happens, in one paragraph

A slider has to know how wide it is before it can decide how wide a card should be. It asks the browser once, when it starts. An element inside display: none has no box at all, so the honest answer is zero, and every card gets sized from that zero. When the tab opens, the cards have no width assigned, so CSS falls back to the full width of the block, which is where the 1060 px number comes from. Opening a tab is not a window resize, so nothing triggers a second measurement. This is not specific to our widget. Any slider-based reviews widget, on any platform, behaves the same way in a hidden container, which is why “my slider is broken inside tabs” is one of the oldest questions on the Elementor forums.

Three fixes, in the order we would try them

1. Do not start the widget until the container is visible. This is the version we would ship. It costs nothing on a normal page, and on a page where the tab is never opened it saves the API call entirely, because the widget never runs.

<div id="justreview-testimonials"></div>
<script>
(function () {
  var box = document.getElementById('justreview-testimonials');
  var started = false;
  function start() {
    if (started || !box.offsetWidth) return;
    started = true;
    JustReview.initTestimonials('YOUR_ACCOUNT_HASH', { /* your settings block */ });
  }
  new ResizeObserver(start).observe(box);
  start();
})();
</script>

Measured with the container hidden at load: nothing renders and no request goes out while it is hidden, and four seconds after the reveal the slider is at 1060 px with 265 px cards, exactly like an ordinary section. On a container that was visible all along it behaves like a plain init call, one API request and the same layout.

2. Force a recalculation after the reveal. If you would rather not touch the init call, add one line to whatever opens the panel:

<script>window.dispatchEvent(new Event('resize'));</script>

Measured: 1060 px and 265 px cards, same as a healthy widget. It is a blunt instrument, because every other script on the page that listens for resize will also run, but on a page whose only sensitive component is the slider it is a two second fix.

3. Update the slider instance directly, if you want to touch nothing else on the page:

<script>
document.querySelector('#justreview-testimonials .testimonials-main-slider')
        .swiper.update();
</script>

We confirmed the instance is reachable that way and that a single update() call restores the 1060 px measurement and the 265 px cards. Fix 1 is still the one to prefer, because it is the only one that never produces a broken frame in the first place.

Popups, and anything that arrives after the page

An Elementor popup is a harsher case than a tab, because the container may not be in the page at all when your code runs. We tested that order deliberately: init first, container inserted two seconds later. The result is worth knowing, because it is invisible.

The widget fetched the reviews from the API, got them, found no container to fill, and rendered nothing. No error, no warning, not a word in the browser console. From the outside it looks like the script failed to load, which sends people to debug the wrong thing.

The rule that follows is simple: the container must exist before the init call runs, and the init call belongs in whatever code runs after the popup content is in the page. One more trap in the same family: if a section injects the whole snippet as a chunk of HTML at runtime, the <script src> inside it never executes. We measured zero requests for the library in that case. A script tag written into the page with innerHTML is inert, by browser design, so the library has to be loaded by the page itself.

A narrow column is its own trap

The other measurable surprise has nothing to do with hiding. We put the same widget in a 400 px column on a 1280 px desktop window:

ContainerSlider widthCards shownCard widthBlock height
Full section, 1140 px1060 px4265 px857 px
Elementor column, 400 px320 px480 px997 px

Four cards at 80 px each, in a column where one card would be readable. The reason is that the number of visible cards comes from a breakpoint measured against the browser window, and the window was 1280 px wide, so the widget used its desktop setting inside a phone-sized column. The block also got 140 px taller, because the text inside those narrow cards wraps.

So in a sidebar or a narrow column, set the visible slides count to 1 or 2 in the widget settings instead of leaving the desktop value, or give the reviews the full width of the section. Which widget shape fits which slot is laid out on the Google reviews widget page, and a compact rating summary is usually the better answer for a narrow column than a squeezed slider.

The desktop and mobile duplicate

Elementor makes it easy to build one section for desktop and a second for mobile, each hidden on the other breakpoint. If you paste the same snippet into both, you get one working widget and one empty div: the code fills the first matching container on the page and stops. We measured exactly that, 20 cards in the first copy, nothing in the second, again with no message anywhere.

Two copies need two names. Give each container its own class and pass that class, with the dot, as the third argument:

<div id="justreview-testimonials" class="copy-desktop"></div>
<script>
  JustReview.initTestimonials('YOUR_ACCOUNT_HASH', { /* settings */ }, '.copy-desktop');
</script>

<div id="justreview-testimonials" class="copy-mobile"></div>
<script>
  JustReview.initTestimonials('YOUR_ACCOUNT_HASH', { /* settings */ }, '.copy-mobile');
</script>

Measured: both copies render, 20 cards each, two API calls. The dot matters. Passing the class name without it targets nothing and you are back to an empty div and a silent console.

Doing it, in the order you will actually do it

  1. Connect a review source in your dashboard and build the widget there, then generate the code. You get three parts: the library, the settings block, and the container div.
  2. Put the library once per page. A site-wide code area is the cleanest place for it. Everything else can live in the Elementor HTML widget.
  3. Drop an HTML widget where the reviews belong and paste the container plus the settings block.
  4. If that HTML widget sits in a tab, an accordion, a popup or any panel that starts closed, use fix 1 above instead of the plain init call.
  5. Preview on the live page, not in the editor. The editor runs its own scripts and is not a fair test of a third-party widget.
  6. Check the mobile breakpoint separately, especially the column width.

The click-by-click version for WordPress, which is where Elementor lives, is in our WordPress install guide, and the wider question of plugin versus snippet is measured in how to add a Google reviews widget to WordPress.

Why we would paste our own widget into that HTML block

Because nothing about it meters your traffic. Several widget tools count monthly widget views and switch the widget off when you pass the limit, which on an Elementor site with reviews in a global footer means the meter runs on every page view you have. We never count views, on any plan, including the free one, and the pricing page says it in the same words. We compared the main tools on that point in the best Google reviews widget in 2026.

The rest is what the measurements above show: one library, one settings block, six widget shapes reading from the same connected sources, and behaviour we can put numbers on when a page does something awkward. You can create an account and generate the snippet in less time than it takes to compare two Elementor addons in the plugin directory.

Two honest caveats. The hidden-container behaviour is ours as much as anyone’s: our widget does not detect the reveal by itself, which is why fix 1 exists and why it is four lines of your code rather than zero. And the visible-slides setting reads the window, not the column, so a narrow column needs a deliberate setting instead of a sensible default.

If you want reviews on an Elementor page today: connect a source, generate the Testimonials snippet, drop it into an HTML widget at full section width, and use the deferred init if it lands anywhere that starts hidden. That is the whole job, and the 265 px number is how you check you got it right.

FAQ

Why does my Elementor reviews slider show only one review?

Almost always because the widget was started while its container was hidden. A slider measures its container once, at startup, and a container inside an inactive tab, a collapsed accordion or an unopened popup measures zero. When the panel is finally revealed the cards fall back to full width, so you get one card where four should be. We measured 1060 px cards instead of 265 px ones, and the layout stayed wrong eight seconds after the reveal.

Can I put the JustReview code in an Elementor HTML widget?

Yes, and that is the intended place for it. Paste the library, the settings block and the container div into one HTML widget, or keep the library in a site-wide code area and only the settings block plus container in the widget. No extra addon is involved either way.

Why is nothing rendered inside my Elementor popup?

Because the container has to exist in the page before the init call runs. We tested the reverse order: the widget still fetched the reviews from the API, found no container to fill, and rendered nothing, without a single message in the browser console. Put the init call inside whatever code runs after the popup content is in the DOM.

Do the reviews get smaller in a narrow Elementor column?

Yes, more than people expect. In a 400 px column on a desktop screen we measured 80 px cards, because the number of visible cards is decided by the browser window width, not by the column. In a narrow column, lower the visible slides setting or give the widget the full section width.

Real reviews, live from three sources

This slider is not a screenshot. It pulls our own reviews from Facebook, Capterra and G2 through the same widget you would install.