Skip to content

RFC: layout prop on Page for repeating page chrome #3499

Description

@diegomura

While working on a major pagination refactor (long running effort, the new engine paginates in a single O(N) pass instead of relayouting pages over and over), I landed on an API idea I want to collect thoughts on before building it: a layout prop on Page.

The idea

You pass a component to Page that describes the repeating chrome of every page. Where it renders {children} is where the page content flows:

const PageLayout = ({ children, pageNumber }) => (
  <>
    <Header title={pageNumber === 1 ? 'Report' : 'Report (cont.)'} />
    <View style={{ flexDirection: 'row', flexGrow: 1 }}>
      <Aside />
      {children}
    </View>
    <Footer>
      <Text render={({ pageNumber }) => `Page ${pageNumber}`} />
    </Footer>
  </>
);

<Page size="A4" layout={PageLayout}>
  {content}
</Page>

Why

fixed is probably the most confused API in react-pdf. It repeats an element on every page, but it doesn't reserve space for it, so footers overlap content unless you compensate with page padding by hand.

With layout the chrome reserves its space by construction. The content region is simply whatever space is left, measured by flexbox like everything else. It also expresses things fixed never could: a sidebar next to the content on every page, or a frame wrapping the content region.

Since the component receives page props, per page variants (different first page, odd/even mirroring, headers that change height) work out of the box.

This is the same model as InDesign parent pages or QuestPDF slots, and the { children } signature should feel familiar from Next.js layouts.

What happens to fixed

It stays, but only for the "render on top of every page" case (watermarks, stamps). layout becomes the story for headers, footers and anything that should take up space. This would ship as part of the pagination rewrite major, so one breaking release, one migration.

Curious what people think, both about the API shape and about use cases it should cover.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions