ScreenshotNeo

BlogHow-to

How to Capture Off-Screen AG Grid Content as an Image

Render virtualized AG Grid rows with print layout, capture them with Playwright, and restore the grid safely afterward.

By the ScreenshotNeo team1 October 20267 min read

Direct answer: AG Grid virtualizes rows and columns, so off-screen cells may not exist in the DOM when you take a normal screenshot. For a modest Client-Side Row Model grid, temporarily switch the grid to domLayout: 'print', remove fixed width and height from the grid element, wait for the redraw and application data to finish rendering, then capture the grid or full page with a browser tool such as Playwright. Restore the original layout in a finally block.

This approach exposes all client-side rows and columns to the browser. A full-page screenshot alone does not disable AG Grid virtualization.

1. Why off-screen AG Grid content is missing

AG Grid normally renders only the visible portion of a large grid and updates the DOM as the user scrolls. A screenshot library can capture pixels only for elements currently rendered. If a row or column has never been materialized, there is nothing for the screenshot to capture. See AG Grid’s documentation on DOM virtualization.

For example, a grid can contain thousands of records while the DOM contains only the rows inside or near the viewport. This is an implementation behavior, not a limit on the number of records your application can hold.

2. The documented print-layout technique

AG Grid’s printing guidance uses print layout to remove the grid’s internal scrolling and expand it to fit its contents. Before changing layout, save the inline dimensions. Clear those dimensions, set print layout, wait for your application’s render lifecycle, capture the result, and restore everything.

const originalWidth = gridElement.style.width;
const originalHeight = gridElement.style.height;

try {
  gridElement.style.width = '';
  gridElement.style.height = '';
  api.setGridOption('domLayout', 'print');

  // Wait for your data, fonts, images, and framework rendering.
  await waitForCaptureReady();
  await page.screenshot({ path: 'ag-grid-full.png', fullPage: true });
} finally {
  api.setGridOption('domLayout', undefined);
  gridElement.style.width = originalWidth;
  gridElement.style.height = originalHeight;
}

The placeholder waitForCaptureReady() must match your application. A fixed delay can be useful as a fallback, but it is not a universal readiness guarantee.

3. Complete Playwright example

The following Node.js script opens a page that exposes the grid API through a test hook, switches to print layout, waits for the expanded grid, captures it, and restores the interactive layout. Adapt the selectors and readiness signal to your application.

import { chromium } from 'playwright';

const browser = await chromium.launch();
const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });

try {
  await page.goto('https://example.com/report', { waitUntil: 'networkidle' });
  await page.locator('#ag-grid').waitFor();

  // Your page should set window.gridApi after creating the grid.
  await page.waitForFunction(() => Boolean(window.gridApi));
  await page.waitForFunction(() => window.reportDataReady === true);

  await page.evaluate(() => {
    const grid = document.querySelector('#ag-grid');
    if (!grid || !window.gridApi) throw new Error('Grid API or element not found');

    window.__captureState = {
      width: grid.style.width,
      height: grid.style.height,
      domLayout: window.gridApi.getGridOption('domLayout')
    };

    grid.style.width = '';
    grid.style.height = '';
    window.gridApi.setGridOption('domLayout', 'print');
  });

  // Replace this with a framework-specific render-complete signal when available.
  await page.waitForTimeout(100);
  await page.evaluate(() => document.fonts?.ready);
  await page.screenshot({ path: 'ag-grid-full.png', fullPage: true });
} finally {
  await page.evaluate(() => {
    const grid = document.querySelector('#ag-grid');
    const state = window.__captureState;
    if (!grid || !state || !window.gridApi) return;

    window.gridApi.setGridOption('domLayout', state.domLayout);
    grid.style.width = state.width;
    grid.style.height = state.height;
  }).catch(() => {});

  await browser.close();
}

Install Playwright with npm install playwright and install a browser with npx playwright install chromium. The page must expose the correct grid API, or you can perform the state change in your application’s own code before invoking the capture worker.

4. Capture only the grid element

Use an element screenshot when the output should contain only the grid. The grid must still be expanded first.

const grid = page.locator('#ag-grid');
await grid.screenshot({ path: 'ag-grid-only.png' });

Use fullPage: true when surrounding headings, filters, or explanatory content belong in the image. Full-page capture captures the page’s scrollable height, but it cannot recover virtualized rows that were never rendered.

5. Readiness and rendering checklist

  • Wait until the Client-Side Row Model has received all intended rows.
  • Wait for sorting, filtering, grouping, and column sizing to finish.
  • Wait for lazy images, web fonts, and framework rendering.
  • Wait for any custom cell renderer that performs asynchronous work.
  • Verify the expanded grid has a nonzero height before capturing.
  • Use a stable application signal when possible, such as window.reportDataReady or a data-capture-ready="true" attribute.

6. Which row models support this method?

Print layout is intended for the Client-Side Row Model. AG Grid states that it does not work for Infinite, Server-Side, or Viewport Row Models because those models load data as needed. For those models, choose one of the alternatives below.

Grid situation Recommended approach Reason
Modest Client-Side Row Model Temporary print layout, then browser screenshot Renders all client-side rows and columns
Infinite, Server-Side, or Viewport Row Model Fetch the intended data and render a separate capture view The normal grid does not contain every row
Very large dataset Split into smaller images or generate an image representation from data Rendering every cell can exhaust time or memory
Paginated document is acceptable AG Grid PDF Export Produces a data representation rather than a pixel-faithful screenshot

7. Large grids and performance

Print layout disables row virtualization and can make the DOM extremely large. AG Grid warns against using this technique for printing when there are many rows or columns. The grid also redraws the entire grid when rows change and removes row animations in print layout.

  • Capture during a background job rather than the interactive user session.
  • Reduce the column set and row count to the requested view.
  • Split a large report into several images with deterministic row ranges.
  • Disable expensive visual effects and unnecessary cell renderers in the capture view.
  • Set a browser timeout and abort captures that exceed your operational limit.
  • Prefer CSV or Excel export when the real requirement is data delivery rather than an image.

8. PDF export versus an image

AG Grid PDF Export follows the current grid state and creates a paginated data representation. AG Grid explicitly describes it as different from a screenshot: it does not reproduce every feature of browser rendering, and it is an Enterprise, module-based feature that requires registering PdfExportModule. Use it when a paginated document is acceptable; use print layout plus a browser screenshot when pixel-level browser output is required. See the PDF Export documentation.

9. Common errors and fixes

Error or symptom Cause Fix
Only visible rows appear Virtualization is still enabled Set domLayout to 'print' and clear fixed dimensions before capture.
The screenshot is blank or very short The grid has not received data or has no computed height Wait for the data-ready signal and verify the grid’s bounding box after switching layout.
Rows are missing in print layout The grid uses Infinite, Server-Side, or Viewport Row Model Fetch data separately and render a dedicated capture view.
Fonts or images differ from the page Resources are still loading Wait for document.fonts.ready and application-specific image readiness.
Capture times out Too many rows, columns, or expensive renderers Reduce the capture, split it, or generate an image directly from the data.
The application remains expanded after capture Cleanup did not run after an exception Put restoration in finally and restore both dimensions and the original layout value.
Rows change during capture Live updates or animations are active Freeze the dataset for the capture and wait for updates to settle.

10. Or skip the browser setup

ScreenshotNeo can capture the rendered page after you make the grid available at its target URL. It removes cookie banners, newsletter popups, and chat widgets before the shot. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers. Its MCP server lets Claude, Cursor, and other MCP clients call take_screenshot, get_page_info, and capture_pdf.

For AG Grid, your page still needs to expose all required rows. You can use a capture-specific route that switches the grid to print layout, or use ScreenshotNeo’s custom JavaScript option to perform that state change before capture. The API supports full-page capture, CSS selectors, waits, custom JavaScript, and other capture controls. See the ScreenshotNeo documentation.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com/report -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://example.com/report"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com/report' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

ScreenshotNeo includes 1,000 screenshots per month free with no card. Paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

11. FAQ

Can a normal full-page screenshot capture every AG Grid row?

No. Full-page capture expands the page image, but it does not force virtualized rows into the DOM.

Does print layout work with server-side data?

No. Use a separate capture view populated with the complete data you intend to show.

Should I leave print layout enabled?

No. It removes virtualization and changes redraw behavior. Enable it only for the capture, then restore the interactive layout.

When should I choose PDF Export?

Choose it when a paginated data document is acceptable. Choose a browser screenshot when you need the rendered page’s visual appearance.

What is the safest way to restore the grid?

Save the original width, height, and domLayout value, and restore all three in a finally block even when capture fails.