GridStack v13.1+ introduces native printing support.
GridStack normally positions widgets with position: absolute and inline top/left coordinates. Browsers
cannot paginate absolutely-positioned content at all - it's removed from the normal document flow, so a
printed grid would either get clipped at the page edge or overlap across pages.
For @media print, GridStack switches widgets to display: block with float: left instead. This puts them
back in the normal document flow, which browsers know how to paginate correctly:
- Items auto-size their height to fit their content (no scrollbars, no clipping - nothing on a printed page is interactive anyway).
- Items flow left to right and wrap to the next line based on their width, in the same order they appear visually on screen.
- A widget that doesn't fit in the remaining space on a page is moved to start cleanly on the next page, instead of being sliced in half across the two pages.
- A hidden widget (
print.hide) takes no space at all, so the widgets that follow move up to reclaim it - there's no empty gap left behind.
The one remaining trade-off: this is a left-to-right/top-to-bottom flow, not a pixel-perfect 2D packing. If one widget in a row is much taller than its neighbors, the next row still can't start higher than that tall widget's bottom edge (plain CSS has no print-safe masonry layout). Hidden widgets aren't affected by this - only different-height visible neighbors sharing a row are.
You can customize how individual widgets print by passing a print object to the widget options.
{
x: 0, y: 0, w: 12, h: 2,
content: 'My Widget',
print: {
hide: true, // Prevent this widget from printing
pageBreak: true, // Force a page break before this widget
orientation: 'landscape', // Force the printed page (and this widget's own page) to be landscape
breakInside: true // Allow this widget's content to fragment across pages instead of
// being kept together as one unbreakable block
}
}Note: pageBreak/orientation/breakInside all pull the widget out of the normal float flow so the browser
reliably honors them (floated elements are otherwise ignored/mishandled by Chrome's print pagination) - for
pageBreak/orientation the widget's width is unaffected, but breakInside also widens the widget to the
full print width (see below).
orientation stays in effect for that page until something else forces a new page - it doesn't automatically
revert on the next widget. If you have a landscape section followed by widgets that should go back to
portrait, set orientation: 'portrait' (or pageBreak: true) explicitly on the widget that should resume
portrait, rather than relying on it reverting on its own.
The base layout keeps every widget together with break-inside: avoid, so a widget that's taller than the
remaining space on a page moves whole to the next page - normally the right behavior, but a widget containing
a long table/datagrid (or a section wrapping a nested sub-grid) can easily be taller than an entire page.
Forcing it to stay in one piece then just leaves the current page mostly blank.
Set print.breakInside: true on that widget to opt out: it's allowed to fragment naturally across as many
pages as it needs, and is widened to the full print width (rather than its on-screen column width) since a
floated box that's allowed to break across pages can otherwise overlap the next floated sibling in Chrome.
{ x: 0, y: 0, w: 6, h: 20, content: '<table>...many rows...</table>', print: { breakInside: true } }A "section" widget - typically a header/title bar followed by a nested sub-grid - has the same needs: the
outer widget should use breakInside: true so the section as a whole can span pages, while the header
itself should never be separated from the row of widgets right after it.
The natural tool for that is break-after: avoid on the header, but Chrome's print engine ignores
break-after/page-break-after set directly on a display: flex (or grid) element - a common case for
headers that lay out a title next to actions/icons. Put the gs-print-avoid-break-after
utility class on a plain block-level wrapper around the header instead of on the flex element itself:
<div class="grid-stack-item-content">
<div class="gs-print-avoid-break-after">
<div class="my-section-header" style="display:flex">Section Title <span>...icons...</span></div>
</div>
<div class="grid-stack">...nested sub-grid widgets...</div>
</div>GridStack also provides a utility class to hide specific elements (like buttons, links, or a navbar) during printing - useful for anything interactive that doesn't make sense on paper, and reverse only during content
<div class="gs-print-hide">
<button onClick="window.print()">Print Dashboard</button>
</div>
<div class="gs-print-show">
content for print only...
</div>This works anywhere in the document, not just on grid items - for example on buttons/links inside a widget's own content.
gs-print-avoid-break-after is another such class - put it on a plain block-level element to stop a page
break from landing right after it. See Keeping a header with the content that follows it
above.