← dompdf.js Studio

Flexbox Layout in dompdf.js: The Complete Rendering Guide

Frontend developers have long taken Flexbox for granted, yet when it comes to generating PDFs, many assume they must fall back to hand-computed coordinates and absolute positioning. dompdf.js parses CSS layout directly and Flexbox is fully supported: flex direction, main-axis and cross-axis alignment, wrapping, flex ratios, and gaps all work according to standard CSS. That means the flex layouts you already write for the web can move into PDF templates unchanged, with toolbars, card lists, and footer bars arranging themselves automatically, no pixel math and no fear that a content length change will collapse the layout. This article starts with the core concepts of Flexbox, the flex container, the flex items, and the two axes, then breaks down every container property and item property as they behave in PDF rendering, with real, runnable code examples. A dedicated section explains how Flexbox cooperates with pagination, covering long lists that span pages and preventing containers from being sliced mid-card, followed by a catalog of common pitfalls and the fallback strategies that keep your templates working in every environment. After this guide you can retire hand-written coordinate PDFs for good and spend your effort on content instead of layout math.You will also learn the practical differences between the web and the PDF world, such as how fixed page width changes flex sizing decisions, how to keep flex containers from breaking awkwardly across pages, and when a table-based fallback is genuinely the safer choice. Every recommendation is paired with a concrete example you can run, and the troubleshooting section is organized by symptom so you can find the fix you need without reading the whole guide again. Whether your templates are simple card rows or complex mixed layouts, the patterns here transfer directly to production code.

Flexbox Core Concepts: Container and Axes

Flexbox consists of a container, an element with display: flex, and its items, the direct children, arranged along two axes: the main axis and the cross axis. The main axis direction is set by flex-direction, row by default for left-to-right arrangement and column for top-to-bottom. Understanding the axes is step one of mastering Flexbox and the foundation for diagnosing any layout anomaly.

dompdf.js applies the standard box model to flex containers: the distribution of items along the main axis is governed by justify-content, and their alignment on the cross axis by align-items. Together these two properties cover centering, edge alignment, and baseline alignment, the same behavior you get in the browser, so web templates migrate without surprises.

Unlike block layout, flex items shrink by default to fit the container, which is where the elastic name comes from. Keep in mind that PDF page width is fixed, so a flex container adapts to the content area width automatically; combined with percentages and automatic sizing, this yields self-adapting layouts that do not care about the concrete page size.

A flex container's default behavior deserves attention: it stretches to fill its parent's width, which is usually what you want in a fixed-width PDF page, but it can surprise you inside narrow columns. When a flex row wraps unexpectedly, check the available width first, then the flex-basis values of its items, because the interaction between the two determines where wrapping begins. Debugging flex is mostly a matter of knowing which of these two variables changed.

The axes also determine which properties control which dimension, a confusion that causes most flex mistakes: in a row layout, width-like behavior comes from flex-basis, while height alignment comes from align-items; in a column layout the roles swap. Once you internalize that the main axis owns sizing and the cross axis owns alignment, flex declarations stop being magic incantations and become predictable tools.

Container Properties in Detail

flex-direction selects the main axis: row suits horizontal button groups and tag rows, column suits vertical info blocks and navigation menus. The reverse values, row-reverse and column-reverse, flip the order for special visual needs such as right-to-left numbered lists, and they behave exactly as the names suggest.

justify-content distributes items along the main axis: flex-start pushes left, center centers, space-between pushes the first and last items to the edges with even spacing between, and space-around plus space-evenly provide the finer-grained distributions. Report headers, card walls, and footer info bars all resolve in one declaration without any manual spacing math.

align-items aligns items on the cross axis: stretch fills the container height by default, center centers, flex-start pins to the top, and baseline aligns to the text baseline. flex-wrap enables wrapping, and gap sets the spacing between items in both axes; gap is the standard property for flex spacing and dompdf.js supports it, so prefer it over per-item margins for cleaner, more maintainable templates.

justify-content values have subtle differences that matter in templates: space-between pushes the first and last items to the edges, while space-around and space-evenly add padding around every item, making the outer gaps different from the inner ones. In footer bars and header rows, space-between is usually the intent, and the other two are for tag clouds and evenly spaced icon rows. If the spacing looks slightly off, check which distribution you actually selected before adjusting margins.

gap is the modern way to space flex items, and it beats margins on every axis because it does not collapse or double. One warning: gap applies between items, not around them, so the first and last items touch the container edge unless you add padding. Pair gap with container padding and you get perfectly regular spacing with no per-item margin math, which keeps templates short and consistent across sections.

Item Properties in Detail

Three properties control an item's elasticity: flex-grow, flex-shrink, and flex-basis, with the shorthand flex: 1 equivalent to flex: 1 1 0%, meaning the item takes an equal share of remaining space, the canonical recipe for equal-width cards. flex: 0 0 auto means no growth and no shrink, so the item keeps its content size.

order reorders items visually without touching the HTML structure, handy for emphasizing a block in templates, and align-self overrides the container-level align-items for a single item, for example text pinned to the top of a row while a button sits at the bottom. This per-item control is what makes mixed-content rows practical.

Understanding how flex-basis relates to width matters: flex-basis is the item's initial main-axis size, while width only acts on the cross axis in row layouts. flex: 1 1 200px means a 200px base that shrinks proportionally when space runs out and grows proportionally when space remains, the core recipe for responsive card grids that adapt to the fixed PDF content width.

The flex shorthand hides a trap for beginners: flex: 1 is not the same as flex: 1 1 auto. The first means flex-basis: 0, so items share space equally from zero, while the second lets content size influence the base. For equal-width cards, flex: 1 1 0% is what you want; for items that should keep a natural size and only shrink when necessary, flex: 0 1 auto is closer. Reading the shorthand as grow, shrink, basis removes the ambiguity permanently.

order is convenient but easy to overuse: reordering several items visually while the DOM stays fixed makes the template hard to reason about, because the source order no longer matches the visual order. Reserve order for small, stable tweaks such as moving a disclaimer below a signature block, and keep the main flow in DOM order. If you find yourself assigning many order values, restructure the HTML instead.

Code Example: Card List and Footer Bar

The example builds three layout patterns in one page: a header row that mixes a heading and a right-aligned badge using margin-left: auto, a wrapping card list with flex: 1 1 200px and gap, and a footer bar using space-between. The margin-left: auto trick is worth memorizing, because it is the flex-native way to push an item to the far end without absolute positioning. Run the code, then change the flex-basis and watch the cards reflow to confirm your mental model of the algorithm.

Notice that the cards wrap with flex-wrap: wrap, so a narrow page stacks them vertically instead of overflowing. That wrapping behavior is exactly what makes flex card lists safe for any page width, and it is the reason flex remains the recommended tool for repeating content even in fixed-page PDFs.

import { DomPDF } from 'dompdf.js';

const html = `
  <style>
    .row { display: flex; align-items: center; }
    .cards { display: flex; gap: 12px; flex-wrap: wrap; }
    .card {
      flex: 1 1 200px;
      border: 1px solid #d5dbe3;
      border-radius: 8px;
      padding: 16px;
      background-color: #fbfcfe;
    }
    .footer {
      display: flex;
      justify-content: space-between;
      border-top: 1px solid #d5dbe3;
      padding-top: 12px;
      margin-top: 24px;
      font-size: 12px;
      color: #666;
    }
    .badge {
      margin-left: auto;   /* push to the far right */
      background-color: #27ae60;
      color: #fff;
      border-radius: 999px;
      padding: 2px 10px;
      font-size: 12px;
    }
  </style>
  <div class='row'>
    <h2>Product Overview</h2>
    <span class='badge'>Best Seller</span>
  </div>
  <div class='cards'>
    <div class='card'><h3>Keyboard</h3><p>Mechanical feel, quiet keys</p></div>
    <div class='card'><h3>Mouse</h3><p>Wireless charging, ergonomic</p></div>
    <div class='card'><h3>Monitor</h3><p>4K wide gamut, eye care</p></div>
  </div>
  <div class='footer'>
    <span>3 products in total</span>
    <span>Total $ 4,299</span>
  </div>`;

const pdf = new DomPDF({ format: 'A4', margin: '20mm' });
pdf.addPage(html, { format: 'A4' });
pdf.save('flexbox-demo.pdf');

Flexbox and Pagination Working Together

Pagination is the biggest difference between PDF and web output: when a flex container holds many items, dompdf.js finds break points between items and splits the container across pages. Give repeating units such as cards a reasonable fixed height so individual items are not sliced mid-card, which keeps every page's layout tidy.

Prevent mid-card slicing with pagination control properties: set an appropriate page-break-inside rule on the card container so a group of cards tends to stay on one page, and give each card its own break-inside rule so a single card never gets cut in half. Long lists paginate noticeably better with these two rules in place.

When a flex container spans pages, its own border and background split across the break, and the bottom border may only appear on the final page. If you need a complete frame on every page, use separate block sections instead, or move page-level decoration into @page margin boxes, and do not assume cross-page container borders behave like single-page ones.

Flexbox and pagination interact in one predictable way: the engine breaks between flex items, so the pagination quality depends on the size of your items. Tall items create coarse breaks, while many small items allow fine-grained breaks that fill pages evenly. If a page ends with a large gap, check whether the next item is simply too tall to fit, and consider splitting oversized items into smaller units or letting them break internally with an appropriate break-inside rule.

Fixed heights on flex items deserve caution in paginated documents: a card with a fixed height that lands near a page boundary either fits entirely or pushes to the next page, potentially leaving a visible gap. Prefer min-height over height for repeatable units, which allows natural compression while guaranteeing a minimum presence. This small change makes long lists paginate more evenly with fewer awkward gaps.

Common Pitfalls and Fallback Strategies

Q: Flex items bunch up and ignore the intended growth? A: Check whether flex-basis or a width constraint is missing; items shrink by default. Give items flex: 1 1 with an explicit value or a min-width so they cannot compress below readable size.

Q: space-between looks uneven in the PDF? A: Confirm the container width is resolved. Without a definite width there is no reference for edge distribution; set width: 100% or an explicit width on the flex container and the distribution matches the browser.

Q: Nested flex layouts render incorrectly? A: Check the alignment and growth settings at every level; most nested issues come from a level missing a width constraint. Give each container an explicit width or flex-basis to isolate the failing level quickly.

Fallback: if a target environment has incomplete flex support, table display (the display: table family) is the most robust replacement for equal-width multi-column layouts, and inline-block with text-align handles simple inline arrangements. The fallback code is longer but functionally equivalent, so use it only where compatibility demands it and prefer flex for new templates.

A frequent complaint is that flex items overflow the page instead of shrinking: this happens when an item's min-width defaults keep it wider than its share of the container. The fix is min-width: 0 on the item, which allows it to shrink below its content width. Long unbroken strings, such as URLs or serial numbers, are the usual culprits, so apply min-width: 0 liberally to any item that can contain long tokens.

When flex behaves differently in the PDF than in the browser, render the same HTML in both and compare the computed widths rather than the visuals. Differences usually trace to fonts: the PDF's font metrics can differ slightly from the browser's, shifting flex-basis calculations and wrap points. If a layout is pixel-sensitive, add small safety margins or switch to percentage bases, which are robust to metric differences.

⚡ 现场演示(点击生成 PDF)

下面的按钮用 dompdf.js 在浏览器端实时生成 PDF,无需后端:

Hello from dompdf.js!

这是由 dompdf.js 渲染的示例 PDF 内容。