diegomura / diegomura/react-pdf

Content taller than one page is squashed/overlapped at the bottom instead of flowing onto the next page

Open
#3,449 4 comments 2 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
16.8k
Forks
1.3k
Avg merge
5h 6m
Merged PRs (30d)
52

Description

# Content overlaps itself at a page break instead of moving to the next page

## Describe the bug

When a block lands near the bottom of a page with *just barely not enough* room to fit, react-pdf force-fits it into the little space left instead of moving it to the next page. Its lines are then drawn on top of each other ("overlapping / scrunched at the page break"), and the rest of the page below is wasted.

It shows up most with two-column rows and `wrap={false}` blocks near a page boundary (headers, footers, itinerary-style entries, table rows).

> The pagination logic lives in **`@react-pdf/layout`**, which `@react-pdf/renderer` depends on. The renderer version can look unrelated (e.g. renderer `4.5.1` → layout `4.6.1`); the bug is in layout **`4.6.1`** and is present on the latest published version.

## How to reproduce

Paste into the [react-pdf REPL](https://react-pdf.org/repl) and look at the **bottom of page 1**. (A plain long `` is *not* enough to trigger it — it paginates cleanly. The trigger is a `wrap={false}` block that is the **sole child of its container** and lands at the bottom of an already-populated page.)

```jsx
const Block = ({ label, value }) => (

{label}
{value}

);

const Entry = ({ time, title, a, w, p }) => (



{time}
PENDING


{title}
2 Adults






);

const Doc = () => (



Daily Itinerary


{/* spacer: pushes the 2nd entry to the bottom of page 1 */}




TUESDAY, APR 7









EXCLUSIVE RESORTS



);

ReactPDF.render();
```

The overlap only happens when the entry lands with just barely not enough room, and the exact height that does so depends on font metrics (they differ slightly between the browser REPL and a server render). The verified window is about height: 490–510; if 500 renders cleanly for you, try 490 or 510.

Expected behavior

The entry should move to the next page and render normally — never overlap its own lines / the footer.

Screenshots

Before (published 4.6.1) — page 1: the "01:05 AM / Miami Beach Golf Club" entry is squashed onto the bottom of page 1; Address sits on top of the street, Website on the URL, Phone on the number.

Image

After (fix in #3450) — page 1 / page 2: page 1 ends cleanly after "Juvia"; the golf entry moves to page 2 and renders normally.

Image

Root cause

In resolvePagination's splitNodes, when a node is split and all of its children move to the next page (leaving an empty shell), the parent is kept on the current page whenever currentChildren.length === 0. That check is meant to mean "the page is empty," but currentChildren is the local list for the node's own container — it is 0 simply because the node is the first child of its container, not because the page is empty. So a node that is the sole child of its container but lands on an already-populated page is force-fit into the space left, and Yoga compresses its lines on top of each other.

Environment

- @react-pdf/renderer: ^4.5.1 (uses @react-pdf/layout 4.6.1)
- Node.js server-side rendering (also reproduces in the in-browser REPL)

Proposed fix

#3450 — decide based on whether the page is actually empty above the node (threaded through the split recursion), so a node with content above it moves to the next page; it is only kept (and allowed to overflow) when the page is genuinely empty, which also avoids an infinite page loop. Includes regression tests.

Contributor guide

Open the contributing guide

Research direction

Start in @react-pdf/layout at resolvePagination's splitNodes logic and review the proposed fix in #3450, including its regression tests. Verify that a sole child with content above it moves to the next page rather than being compressed, while a genuinely empty page still avoids an infinite loop.

Written by the indexing model from the issue text.

Assessment

Tech stack
react, typescript
Domain
tooling
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.