How Does Layout Work in react-pdf?

Updated

react-pdf uses Yoga to bring flexbox layout to PDF pages, so you position elements with the same mental model you already use for CSS: rows, columns, wrapping, and absolute positioning. If you are comfortable with flexbox in the browser, the main adjustment is that you are laying out fixed-size pages rather than a scrolling viewport, and you build the structure with React components instead of HTML tags.

The layout engine: Yoga, not the browser

react-pdf does not run your document through a browser rendering engine. It uses Yoga, a cross-platform layout engine that implements flexbox, to compute positions and sizes. That has two practical consequences:

  • Flexbox is the primary layout tool. You get flexDirection, justifyContent, alignItems, flexWrap, flex, and related properties on View components.
  • There is no CSS cascade or stylesheet. Styles are JavaScript objects passed to a style prop, and they are resolved per component rather than inherited through a global cascade.

Because the same document definition renders to a file, a stream, or a blob — in Node or in the client — the layout result is consistent across server and browser, which matters when you generate the same invoice or report in both places.

Building a page structure with React components

The layout primitives are React components, not HTML elements:

Component Role in layout
Document Root container; holds page-level metadata
Page A fixed-size canvas; you set size and style here
View The flex container/box — your main layout building block
Text Text content, which must live inside a Text or View context

A minimal structure looks like this:

import { Document, Page, View, Text, StyleSheet } from '@react-pdf/renderer';

const styles = StyleSheet.create({
  page: { flexDirection: 'column', padding: 30 },
  row: { flexDirection: 'row', justifyContent: 'space-between' },
  column: { flexGrow: 1, flexBasis: 0 },
});

const Invoice = () => (
  <Document>
    <Page size="A4" style={styles.page}>
      <View style={styles.row}>
        <View style={styles.column}><Text>From</Text></View>
        <View style={styles.column}><Text>To</Text></View>
      </View>
    </Page>
  </Document>
);

The input is the component tree plus the style objects; the action is rendering; the expected result is a laid-out page where the two columns split the available width evenly. Note that flexDirection defaults to column in react-pdf, unlike the browser default of row — a common source of surprise when porting CSS.

What carries over from CSS flexbox

  • Rows and columns: flexDirection: 'row' | 'column' controls the main axis.
  • Distribution: justifyContent and alignItems behave as you expect along the main and cross axes.
  • Wrapping: flexWrap: 'wrap' lets children flow onto multiple lines.
  • Absolute positioning: position: 'absolute' with top, left, right, bottom takes an element out of flow — useful for headers, footers, and watermarks.
  • Sizing: width, height, flexGrow, flexShrink, and flexBasis work on View boxes.

Where it differs from the browser

  • No scrolling viewport. A Page is a fixed canvas. Content that overflows does not scroll; you control it with page breaks.
  • Page-break control is explicit. The docs list page-break control as a feature, so you manage where content splits rather than relying on the browser.
  • Units and defaults differ. Sizes are in points, and as noted, the default flex direction is column.
  • Text is special. Text must be wrapped in Text components; you cannot drop raw strings into a View and expect them to lay out like inline HTML.

Beyond boxes: fonts, images, and SVG

Layout is not only about boxes. The renderer supports custom fonts, images, SVG, form fields, and hyphenation, all of which participate in the same flexbox flow. An image inside a View with flexGrow will size according to the flex rules, and hyphenation affects how text wraps inside its container — so text-heavy layouts benefit from testing with your actual font and content length.

A practical way to verify layout

  1. Install the renderer: npm install @react-pdf/renderer.
  2. Build a small document with one Page and a couple of nested View rows.
  3. Render it to a file or blob and open the result.
  4. Adjust flexDirection, justifyContent, and flexBasis until the boxes land where you want.

The playground linked from the site is the fastest loop for this, since you can change styles and see the page without wiring up a full build.

If your goal is a document whose structure mirrors a familiar CSS flexbox layout — rows, columns, wrapping, absolute overlays — react-pdf is a direct fit. If you need a scrolling, reflowing web page rather than fixed pages, or you depend on the full CSS cascade, flexbox alone will not cover that, and you should treat the layout model as a constrained subset rather than a browser equivalent.