Implementing User-Controlled Height for Looker Studio Embedded Reports

6 min read

Looker Studio’s embedded reports present a common challenge: dynamic height adjustment. The official Embed SDK’s withDynamicIFrameHeight() method is sparsely documented and frequently fails to fire events, leaving developers with stubborn scrollbars or truncated content. This guide provides a robust, client-side alternative that empowers end-users to control report height while persisting their preferences.

The solution comprises three files: a JavaScript module, a stylesheet, and an HTML snippet. It requires no external dependencies, respects user preferences via localStorage, and gracefully handles mobile and desktop viewports.

File Structure

Organise your assets as follows:

/project-root
├── index.html
├── css/
│   └── iframe-resizer-lookerstudio.css
└── js/
    └── iframe-resizer-lookerstudio.js

Step-by-Step Integration

Step 1: Save the JavaScript Module

Copy the code below into js/iframe-resizer-lookerstudio.js. This module creates the iframe, injects height controls, and manages localStorage persistence.

No modification is required. The function detects viewport width on load and sets sensible defaults: 600 px for mobile, 500 px for tablet, and 700 px for desktop.

function embedLookerWithControls(reportUrl, containerId) {
  const container = document.getElementById(containerId);
  if (!container) throw new Error(`Container "${containerId}" not found`);

  // Clear container and create wrapper
  container.innerHTML = '';
  container.style.cssText = 'width:100%;';

  // Create controls FIRST (will appear at top)
  const controls = document.createElement('div');
  controls.id = 'looker-controls';
  controls.style.cssText = ``;

  controls.innerHTML = `
    <label>Report Height:</label>
    <button id="height-down">−</button>
    <input type="number" min="600" max="3000" step="100" value="700" id="height-input">
    <button id="height-up">+</button>
    <button id="height-save">Save</button>
    <span id="save-status" style="color:green; display:none; margin-left:8px;">✓ Saved!</span>
  `;

  // Create iframe container (below controls)
  const iframeWrapper = document.createElement('div');
  iframeWrapper.id = 'looker-iframe-wrapper';
  iframeWrapper.style.cssText = 'width:100%; position:relative;';

  const iframe = document.createElement('iframe');
  iframe.src = reportUrl.trim();
  iframe.style.cssText = 'width:100%; border:0; display:block;';

  // Set initial height based on device
  const getDefaultHeight = () => {
    if (window.innerWidth < 480) return 600;
    if (window.innerWidth < 1024) return 500;
    return 700;
  };

  const initialHeight = getDefaultHeight();
  iframe.style.height = `${initialHeight}px`;
  controls.querySelector('#height-input').value = initialHeight;
  iframeWrapper.appendChild(iframe);

  // Append controls FIRST, then iframe
  container.appendChild(controls);
  container.appendChild(iframeWrapper);

  // Control logic
  function updateHeight(value) {
    iframe.style.height = `${value}px`;
    controls.querySelector('#height-input').value = value;
  }

  controls.querySelector('#height-down').onclick = () => {
    const input = controls.querySelector('#height-input');
    input.stepDown();
    updateHeight(input.value);
  };

  controls.querySelector('#height-up').onclick = () => {
    const input = controls.querySelector('#height-input');
    input.stepUp();
    updateHeight(input.value);
  };

  controls.querySelector('#height-input').onchange = (e) => {
    updateHeight(e.target.value);
  };

  controls.querySelector('#height-save').onclick = () => {
    const height = iframe.style.height;
    localStorage.setItem('lookerHeight_90338fb3', height);
    const status = controls.querySelector('#save-status');
    status.style.display = 'inline';
    setTimeout(() => status.style.display = 'none', 2000);
  };

  // Load saved preference
  const saved = localStorage.getItem('lookerHeight_90338fb3');
  if (saved) {
    iframe.style.height = saved;
    controls.querySelector('#height-input').value = parseInt(saved);
  }
}

Step 2: Save the Stylesheet

Copy the CSS below into css/iframe-resizer-lookerstudio.css. This styles the control bar and ensures it sits flush against the iframe.

The controls are right‑aligned for minimal visual interference, with a subtle grey background that blends with most dashboards.

/*
Auto Height CSS & Controls for embeeded iframe view using Javascript
*/
#looker-container {
margin:0;
}
#looker-controls {
padding: 5px;
background: rgb(240, 240, 240);
border: 0px solid rgb(204, 204, 204);
margin-bottom: 0;
display: flex;
align-items: center;
gap: 8px;
font-family: system-ui;
border-radius: 0;
justify-content: flex-end;
}
#looker-controls label {
font-weight: 500;
margin-right: 5px;
}
#height-down, #height-up{
padding: 0px 5px;
cursor: pointer;
border: 1px solid #999;
}
#height-input {
width: 65px;
padding: 1px 5px;
font-size: 14px;
}
#height-save {
padding: 1px 10px;
background: #1a73e8;
color: white;
border: none;
cursor: pointer;
}

Step 3: Integrate into Your HTML Page

In your HTML file, link the stylesheet, include the script, and add a container <div> with the ID looker-container. Initialise the embed once the DOM is ready.

Replace YOUR_EMBED_URL_HERE with your Looker Studio embed URL. This is the only value you must edit.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Looker Studio Report</title>

  <!-- 1. Link stylesheet -->
  <link media="all" rel="stylesheet" href="./css/iframe-resizer-lookerstudio.css" />
</head>
<body>

  <!-- 2. Add container where report will appear -->
  <div id="looker-container" style="width:100%;"></div>

  <!-- 3. Include JavaScript module -->
  <script src="./js/iframe-resizer-lookerstudio.js"></script>

  <!-- 4. Initialise embed (edit the URL) -->
  <script>
    document.addEventListener('DOMContentLoaded', () => {
      embedLookerWithControls(
        'YOUR_EMBED_URL_HERE',
        'looker-container'
      );
    });
  </script>

</body>
</html>

Step 4: Obtain Your Looker Studio Embed URL

  1. Open the report in Looker Studio.
  2. Click FileEmbed report.
  3. Enable Embed.
  4. Copy the URL from the Embed URL field.
  5. Paste it into the HTML snippet above, replacing YOUR_EMBED_URL_HERE.

Important: Ensure the URL contains /embed/reporting/ and ends with a page ID (e.g., /page/SZddfs). Do not use the sharing link or the standard report URL.

Notes & Troubleshooting

Issue 1: Controls Do Not Appear

Cause: The container ID does not match.
Fix: Verify that document.getElementById('looker-container') exists in your DOM and that the ID is unique.

Issue 2: Height Resets on Page Refresh

Cause: localStorage is disabled or cleared on exit.
Fix: Check browser privacy settings. The solution uses localStorage, which requires cookie-like storage to be enabled.

Issue 3: Report Still Shows Scrollbars

Cause: The default height is insufficient for your report’s content.
Fix: Adjust the getDefaultHeight values in the JavaScript file, or manually increase the height using the controls and click Save.

Issue 4: Controls Overlap Report on Mobile

Cause: The iframe wrapper lacks breathing room.
Fix: Add margin-top: 8px; to #looker-iframe-wrapper in the CSS file.

Issue 5: Console Error: "Container not found"

Cause: The script runs before the DOM element exists.
Fix: Ensure document.addEventListener('DOMContentLoaded', ...) wraps the embedLookerWithControls call.

Why This Approach?

Unlike fragile event-listening methods, this solution:

  • Works with any Looker Studio report, irrespective of event emissions.
  • Persists user preferences across sessions.
  • Avoids CORS pitfalls by not probing the iframe’s document.
  • Gracefully degrades to sensible defaults if JavaScript fails.

The trade‑off is minimal: a discreet control bar that end‑users rarely need after saving their preference once.

Browser Support

  • JavaScript: ES6 syntax (const, arrow functions). Use a transpiler like Babel for IE11.
  • CSS: Flexbox and localStorage. Supported in all modern browsers and IE10+.
  • Tested browsers: Chrome 90+, Firefox 88+, Safari 14+, Edge 90+.