How to break a page conditionally with react-pdf/renderer

5.7k Views Asked by At

I am trying to add a page break conditionally once my Section title is not on the first page, i.e., page breaks should begin after the second page. How do I add this condition to my View tag for the "Section Title" to my code below where there is break property should be applied from the 2nd page?

IMPORTANT: This code is not a React component. This code is a function that is called when a button is clicked from a different react component. The GeneratePDF is added to an onClick property; when a person clicks the button, the pdf will be generated.

I am using react-pdf/renderer. Please see my code below, where the page break starts from page 1.

GeneratePDF.jsx

import { saveAs } from 'file-saver';
import { pdf, Document, Page, Text, View, Image } from '@react-pdf/renderer';
import styles from './styles';

const GeneratePDF = async (fileName, data) => {
  const blob = await pdf((
    <Document>
      <Page style={styles.body}>
        <View style={styles.root}>
          <Text style={styles.headerTitle}>
            Header Title
          </Text>
          <Image
            style={styles.image}
            src="someImage.jpg"
          />
        </View>

        {data.map((item, index) => (
          <View key={item}>
            <View break wrap={false}>
              <Text style={styles.sectionTitle}>Section Title</Text>
            </View>
            <View wrap={false}>
              <Text style={styles.subtitle}>About</Text>
              <Text style={styles.aboutDesc}>{item.description}</Text>
            </View>
            <View>
              <Text>{'\n'}</Text>
              <Text style={styles.subtitle}>Things wanted</Text>
              {item.things.map((thing, idx) => (
                <Text key={thing} style={styles.list}>
                  -
                  {thing.description}
                </Text>
              ))}
            </View>
          </View>
        ))}

        <Text
          style={styles.pageNumber}
          render={({ pageNumber, totalPages }) => (
            `${pageNumber} / ${totalPages}`
          )}
          fixed
        />
      </Page>
    </Document>
  )).toBlob();
  
  saveAs(blob, fileName);
};

export default GeneratePDF;

2

There are 2 best solutions below

0
On BEST ANSWER

The simplest way to do this was, since I wanted each new SectionTitle on a new page, this is the way I did it using the index from the map, break={index > 0}. This ensures that my page break starts only from the 2nd item.

        {data.map((item, index) => (
          <View key={item}>
            <View break={index > 0} wrap={false}>
              <Text style={styles.sectionTitle}>Section Title</Text>
            </View>
            ...
          </View>
        ))}
1
On

To add a page break conditionally in your code, you can use the pageBreak prop of the View component. This prop takes a string value that specifies where the page break should be inserted.

To insert a page break after the second page, you can do the following:

  1. Add a state variable called pageNumber to keep track of the current page number. Initialize this variable to 1.
  2. Inside the map function that is iterating over the data array, add a condition to check if the current page number is greater than 1. If it is, then set the pageBreak prop of the View component that contains the "Section Title" to "after".
  3. Increment the pageNumber variable by 1 after rendering each "Section Title" View component.

Here's how your code would look with these changes:

import { saveAs } from 'file-saver';
import { pdf, Document, Page, Text, View, Image } from '@react-pdf/renderer';
import styles from './styles';

const GeneratePDF = async (fileName, data) => {
  const [pageNumber, setPageNumber] = useState(1);

  const blob = await pdf((
    <Document>
      <Page style={styles.body}>
        <View style={styles.rootContainer}>
          <Text style={styles.headerTitle}>
            Header Title
          </Text>
          <Image
            style={styles.image}
            src="someImage.jpg"
          />
        </View>

        {data.map((item, index) => (
          <View key={item}>
            <View wrap={false} pageBreak={pageNumber > 1 ? 'after' : 'none'}>
              <Text style={styles.sectionTitle}>Section Title</Text>
            </View>
            <View wrap={false}>
              <Text style={styles.subtitle}>About</Text>
              <Text style={styles.aboutDesc}>{item.description}</Text>
            </View>
            <View>
              <Text>{'\n'}</Text>
              <Text style={styles.subtitle}>Things wanted</Text>
              {item.things.map((thing, idx) => (
                <Text key={thing} style={styles.list}>
                  -
                  {thing.description}
                </Text>
              ))}
            </View>
            {setPageNumber(pageNumber + 1)}
          </View>
        ))}

        <Text
          style={styles.pageNumber}
          render={({ pageNumber, totalPages }) => (
            `${pageNumber} / ${totalPages}`
          )}
          fixed
        />
      </Page>
    </Document>
  )).toBlob();
  
  saveAs(blob, fileName);
};

export default GeneratePDF;