DEV Community

IronSoftware
IronSoftware

Posted on

@react-pdf/renderer in 2026: The StyleSheet Isn't CSS

Why does a React layout that works in the browser render differently once it is a PDF? Because @react-pdf/renderer's StyleSheet looks like CSS without being CSS. flexDirection does not default to row the way a browser does, CSS Grid is not supported at all, and the properties that resemble CSS are a subset of it.

The component model fits a React team's workflow. IronPDF for Node.js renders real CSS, Grid included, through a browser engine, which is where the two part ways.

Full disclosure. We build IronPDF for Node.js at Iron Software, and this read looks at where @react-pdf/renderer's CSS subset costs a React team and where IronPDF renders real CSS from the same markup.

What @react-pdf/renderer does well

For a React team, this is a natural way to work. A document is built from components and a StyleSheet that reads like CSS, the same way the rest of the app is.

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

const styles = StyleSheet.create({
  row: { flexDirection: "row", justifyContent: "space-between" },
});

const Invoice = () => (
  <Document><Page><View style={styles.row}>
    <Text>Invoice #1042</Text><Text>$250.00</Text>
  </View></Page></Document>
);
Enter fullscreen mode Exit fullscreen mode

For a React team building documents from scratch, that model is a workflow no HTML-rendering tool provides. A document is JSX, reviewed in a normal pull request, and previewable live while you build it, with no browser step and no separate template file to keep in sync with the components around it. It is MIT-licensed, it has no CVEs on record across the GitHub Advisory Database, NVD, and Snyk, and it renders through PDFKit internally, which is itself clean, so there is nothing to flag on either side. It is among the more actively maintained libraries in this space, carrying the largest star and open-issue count of any library here, which reads as a large, engaged user base filing edge cases rather than instability, with a release and commits landing within days of this writing.

Where the StyleSheet stops being CSS

The expectation it sets is where teams lose time, since it reads like CSS closely enough to assume full CSS behaviour transfers. The gaps come in three places.

  • It does not render HTML or CSS, it transforms a component tree through PDFKit: an existing web component or stylesheet cannot be dropped in, so every document is rebuilt from this library's own View, Text, and Image primitives rather than reused from the app it is meant to complement, and a design system already expressed in CSS is re-expressed in the subset from scratch.
  • The styling layer is a CSS subset, not CSS: @react-pdf/renderer defaults flexbox on, but flexDirection must be set explicitly because the default is not the browser's row, which catches developers assuming browser behaviour transfers, and open issues document flexbox edge cases such as flex: 1 producing overlapping content.
  • CSS Grid is not supported at all: @react-pdf/renderer has no Grid, so for a team used to building layouts with Grid, that is not a smaller version of the feature, it is absent, and a Grid layout has to be rebuilt in flexbox from scratch.

The debugging is what makes it costly. Someone ports a browser layout into View and Text with matching StyleSheet properties, and it renders, just not the way it did in the browser. The property that broke it is one assumed to be standard CSS that behaves differently here or is not implemented, and since the render still succeeds, no error points at which one.

Real CSS, Grid included

IronPDF renders HTML and CSS through Chromium, so Grid is Grid and flexbox behaves the way a browser lays it out, with no subset to work around and no primitives to rebuild the design in.

import { PdfDocument } from "@ironsoftware/ironpdf";

const invoice = await PdfDocument.fromHtml(`
  <style>.invoice { display: grid; grid-template-columns: 1fr 1fr; }</style>
  <div class="invoice"><span>Invoice #1042</span><span>$250.00</span></div>
`);

await invoice.saveAs("invoice.pdf");
Enter fullscreen mode Exit fullscreen mode

That renders a Grid layout the same way the browser does, from the markup the app already has, and every property behaves the way it does in a browser because it is one, so there is no subset to memorise and no default that differs from row. The HTML-to-PDF guide covers it end to end.

@react-pdf/renderer and IronPDF for Node.js, side by side

Capability @react-pdf/renderer IronPDF for Node.js
Document model Components, View and Text HTML and CSS, PdfDocument.fromHtml
Rendering engine PDFKit underneath Chromium
Reuse existing HTML or CSS Not possible Direct, it is the input
Flexbox Subset, non-browser default Full, via Chromium
CSS Grid Not supported Supported

Table 1. @react-pdf/renderer and IronPDF for Node.js on the styling questions, from the project's own documentation and issue tracker.

When the layout needs real CSS

The one case for @react-pdf/renderer is a React team building documents from scratch with layouts flexbox can express, where the component workflow and live preview are the draw. That holds until a design needs Grid, or needs to reuse an existing HTML component the app already has, though, and few designs stay inside a flexbox-only subset for long, since neither is something a CSS subset over PDFKit can do.

Once the layout reaches past what the subset covers, IronPDF for Node.js renders it from real CSS, including the HTML those components already output, so the move is a change of renderer rather than a rewrite of the design.

Has your team hit the flexDirection default surprise yet, or are you building layouts that need Grid? Tell us in the comments, we would like to know which one trips people up more.

@react-pdf/renderer is an open-source project distributed under the MIT licence, and is not affiliated with Iron Software. The details above are drawn from the project's own documentation and issue tracker. If something has changed since, correct us in the comments.

Top comments (0)