You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用@react-pdf/renderer仅在PDF第一页显示图片?

问题:@react-pdf/renderer中仅在第一页显示图片失败

我在React应用中使用@react-pdf/renderer开发PDF生成功能,需要仅在PDF的第一页显示图片及部分文本,但图片完全不显示。我尝试通过给View组件添加render prop,根据页码条件渲染Image组件,但实际没有效果。以下是我的代码:

import { Document, Page, Text, View, StyleSheet, pdf, Image } from '@react-pdf/renderer'
import moment from 'moment'
import { headerImgPdfQuote } from '../../../Clients/devisTemplate'

const styles = StyleSheet.create({
  page: {
    flexDirection: 'column',
    backgroundColor: '#FFFFFF',
    padding: '0 40px'
  },
  header: {
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0
  },
  headerImage: {
    width: '100%',
    height: 'auto'
  }
})

const QuotePDF = () => (
  <Document>
    <Page size="A4" style={styles.page} wrap>
      <View style={styles.header}>
        <View render={({ pageNumber }) => (
          pageNumber === 1 && (
            <Image src={headerImgPdfQuote} style={styles.headerImage} />
          )
        )} />
        <View render={({ pageNumber }) => (
          pageNumber === 1 && (
            <Text>Reference: {quoteData.ref}</Text>
          )
        )} />
        <View render={({ pageNumber }) => (
          pageNumber === 1 && (
            <Text>{moment(quoteData.createdAt).format('DD/MM/YYYY')}</Text>
          )
        )} />
      </View>
      <Text render={({ pageNumber, totalPages }) => `${pageNumber}/${totalPages}`} fixed />
    </Page>
  </Document>
)

const generatePDF = async () => {
  const blob = await pdf(<QuotePDF />).toBlob()
  const url = URL.createObjectURL(blob)
  const link = document.createElement('a')
  link.href = url
  link.download = `quote_${moment(quoteData.createdAt).format('DD_MM_YYYY')}.pdf`
  link.click()
}

其中headerImgPdfQuote是经过base64编码的图片(已转为完整的data URI格式)。请问我哪里出错了,如何确保图片仅在第一页显示?


解决方案

错误原因

View组件并不支持通过render prop获取当前页码(pageNumber),只有Page、Text等特定组件支持该特性。你给View添加的render prop不会被触发,导致条件渲染逻辑完全失效,图片自然无法显示。

修正方案

方案1:通过Page组件的render prop实现条件渲染

直接在Page的render回调中获取pageNumber,然后根据页码判断是否渲染头部内容:

import { Document, Page, Text, View, StyleSheet, pdf, Image } from '@react-pdf/renderer'
import moment from 'moment'
import { headerImgPdfQuote } from '../../../Clients/devisTemplate'

const styles = StyleSheet.create({
  page: {
    flexDirection: 'column',
    backgroundColor: '#FFFFFF',
    padding: '0 40px',
    // 给头部留出空间,避免内容被绝对定位的header遮挡
    paddingTop: 100 
  },
  header: {
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    padding: '0 40px' // 和page的padding保持一致
  },
  headerImage: {
    width: '100%',
    height: 'auto'
  }
})

const QuotePDF = () => (
  <Document>
    <Page 
      size="A4" 
      style={styles.page} 
      wrap
      render={({ pageNumber, totalPages }) => (
        <>
          <View style={styles.header}>
            {/* 仅在第一页渲染头部内容 */}
            {pageNumber === 1 && (
              <>
                <Image src={headerImgPdfQuote} style={styles.headerImage} />
                <Text>Reference: {quoteData.ref}</Text>
                <Text>{moment(quoteData.createdAt).format('DD/MM/YYYY')}</Text>
              </>
            )}
          </View>
          <Text fixed>{`${pageNumber}/${totalPages}`}</Text>
        </>
      )}
    />
  </Document>
)

const generatePDF = async () => {
  const blob = await pdf(<QuotePDF />).toBlob()
  const url = URL.createObjectURL(blob)
  const link = document.createElement('a')
  link.href = url
  link.download = `quote_${moment(quoteData.createdAt).format('DD_MM_YYYY')}.pdf`
  link.click()
}

方案2:使用usePage钩子封装头部组件

如果需要复用头部逻辑,可以使用@react-pdf/renderer提供的usePage钩子(确保你的版本支持该API):

import { Document, Page, Text, View, StyleSheet, pdf, Image, usePage } from '@react-pdf/renderer'
import moment from 'moment'
import { headerImgPdfQuote } from '../../../Clients/devisTemplate'

const styles = StyleSheet.create({
  page: {
    flexDirection: 'column',
    backgroundColor: '#FFFFFF',
    padding: '0 40px',
    paddingTop: 100
  },
  header: {
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    padding: '0 40px'
  },
  headerImage: {
    width: '100%',
    height: 'auto'
  }
})

// 封装仅在第一页显示的头部组件
const FirstPageHeader = () => {
  const { pageNumber } = usePage()
  if (pageNumber !== 1) return null

  return (
    <>
      <Image src={headerImgPdfQuote} style={styles.headerImage} />
      <Text>Reference: {quoteData.ref}</Text>
      <Text>{moment(quoteData.createdAt).format('DD/MM/YYYY')}</Text>
    </>
  )
}

const QuotePDF = () => (
  <Document>
    <Page size="A4" style={styles.page} wrap>
      <View style={styles.header}>
        <FirstPageHeader />
      </View>
      <Text render={({ pageNumber, totalPages }) => `${pageNumber}/${totalPages}`} fixed />
    </Page>
  </Document>
)

const generatePDF = async () => {
  const blob = await pdf(<QuotePDF />).toBlob()
  const url = URL.createObjectURL(blob)
  const link = document.createElement('a')
  link.href = url
  link.download = `quote_${moment(quoteData.createdAt).format('DD_MM_YYYY')}.pdf`
  link.click()
}

额外注意事项

  • 确保headerImgPdfQuote是完整的data URI格式,例如:data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...
  • 给page样式添加paddingTop,避免页面内容被绝对定位的头部遮挡
  • 头部的padding要和页面的padding保持一致,保证内容对齐

内容的提问来源于stack exchange,提问作者Dani

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 01:00:56