Cómo Convertir HTML a PDF en React

Github

En ocasiones necesitamos exportar un contenido de nuestra página HTML a PDF con la librería React.

Para hacer esto debes seguir ciertos pasos importantes.

En este tutorial te enseñaré a Cómo Convertir HTML a PDF en React, vamos con ello.

HTML convertido a PDF con la librería React
Con React podemos convertir HTML a PDF de forma rápida

Convirtiendo HTML a PDF con React

Sigue los pasos que te indico a continuación para que todo salga bien:

Creación de nuevo proyecto

Crearé un nuevo proyecto de React con la herramienta Vite, ejecutando el siguiente comando (He colocado comentarios para explicar paso a paso el proceso de creación del proyecto):

# Comando para crear el proyecto
npm create vite@latest

> npx
> create-vite

# Le damos un nombre al proyecto
? Project name: » vite-project miapp
√ Project name: ... miapp

# Seleccionamos React
? Select a framework: » - Use arrow-keys. Return to submit.
    Vanilla
    Vue
>   React
    Preact
    Lit
    Svelte
    Solid
    Qwik
    Others
√ Select a framework: » React

# Elijo TypeScript + SWC 
? Select a variant: » - Use arrow-keys. Return to submit.
    TypeScript
>   TypeScript + SWC
    JavaScript
    JavaScript + SWC
    Remix ↗
√ Select a variant: » TypeScript + SWC

Puedes ver que al final elegí la variante TypeScript + SWC.

Esta variante es muy potente, en este artículo te explico porque lo és.

Ingresamos al directorio del proyecto e instalamos las dependencias necesarias:

# Ingresamos al directorio del proyecto
cd miapp

# Instalamos las dependencias
npm install 

# Verificar que el proyecto se creo correctamente (Iniciar servidor de Vite)
npm run dev

Puedes ver que sobre el final he colocado el comando npm run dev.

Puedes ejecutarlo, para verificar que todo este bien.

Convertir HTML a PDF con React

Instalamos la librería React-pdf, ejecutando el siguiente comando:

npm install @react-pdf/renderer --save

added 61 packages, and audited 206 packages in 5s

40 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

Al comando anterior le he pasado la opción –save para registrar la dependencia en el archivo package.json del proyecto.

De esta manera tenemos un control de lo que instalamos en nuestro proyecto.

Importamos React

Abrimos el archivo App.tsx y comenzamos importando React:

// Importamos React y lo hacemos global
import React from 'react'
import { render } from 'react-dom'
window.React = React

Imagen y Fuente para el PDF

Importamos una imagen y un tipo de letra para el contenido del PDF:

// Importamos una imagen local y una fuente
import miimagen from './assets/images/como-usar-axios-y-angular.jpg'
import mifuente from './assets/fonts/SUSE-VariableFont_wght.ttf'

Instanciamos React-pdf

Importamos la librería React-pdf algunos elementos necesarios:

// Importamos 'React-pdf' con sus elementos necesarios
import {
  Document,
  Page,
  Text,
  Image,
  Font,
  StyleSheet,
  PDFViewer,
  PDFDownloadLink,
  View,
} from "@react-pdf/renderer";

Configuración de la fuente para el PDF

Definimos la fuente que usaremos el  texto del PDF:

// Personalizamos la fuente del PDF
Font.register({
  family: 'SUSE',
  src: mifuente
});

Estilos para el PDF

Le damos algunos estilos CSS a nuestro PDF:

// Estilos CSS para nuestro PDF
const estilos = StyleSheet.create({
  page: {
    backgroundColor: "#d11fb6",
    color: "white",
  },
  section: {
    margin: 10,
    padding: 10,
  },
  viewer: {
    width: 800, 
    height: 400,
    marginBottom: 30,
  },
  body: {
    paddingTop: 35,
    paddingBottom: 65,
    paddingHorizontal: 35,
  },
  title: {
    fontSize: 24,
    textAlign: 'center',
    fontFamily: 'SUSE'
  },
  author: {
    fontSize: 12,
    textAlign: 'center',
    marginBottom: 40,
  },
  subtitle: {
    fontSize: 18,
    margin: 12,
    fontFamily: 'SUSE'
  },
  text: {
    margin: 12,
    fontSize: 14,
    textAlign: 'justify',
    fontFamily: 'Times-Roman'
  },
  image: {
    marginVertical: 15,
    marginHorizontal: 100,
  },
  header: {
    fontSize: 12,
    marginBottom: 20,
    textAlign: 'center',
    color: 'grey',
  },
  pageNumber: {
    position: 'absolute',
    fontSize: 12,
    bottom: 30,
    left: 0,
    right: 0,
    textAlign: 'center',
    color: 'grey',
  },
});

Contenido del PDF

Agregamos los elementos que tendrá el contenido del PDF:

// Función para el contenido del PDF en el Visor
function DocumentoPDF() {
  
  return (
    <PDFViewer style={estilos.viewer}>
      {/* Iniciamos el documento */}
      <Document>
        {/* Renderizamos las páginas del PDF */}
        <Page style={estilos.body} size="A4">
          <View>
            {/* Página 1 */}
            <Text style={estilos.title}>Mi Bonito PDF</Text>
            <Text style={estilos.author}>Por Nube Colectiva</Text>
            <Image
              style={estilos.image}
              src={miimagen}
            />            
            <Text style={estilos.subtitle}>
              Introducción
            </Text>
            <Text style={estilos.text}>
              Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus varius varius convallis. Cras vel nibh rhoncus eros consequat varius. Phasellus imperdiet dapibus purus a condimentum. Curabitur vehicula, massa vitae pulvinar laoreet, enim enim facilisis augue, eget fringilla ipsum urna sit amet risus. Maecenas consequat nulla sit amet quam bibendum, sit amet elementum odio hendrerit. Nunc eget congue risus, ut posuere mi. Nullam vehicula tellus elit, at varius dui ultricies eu. Nullam scelerisque in sem a egestas. Aenean convallis mauris eu lacus rutrum lobortis. Vestibulum vulputate efficitur augue, in rhoncus risus bibendum eu. Suspendisse vel mollis urna, placerat tempus magna. Integer ex tellus, vulputate non dictum ac, posuere vel justo.
            </Text>
            <Text style={estilos.text}>
              Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus varius varius convallis. Cras vel nibh rhoncus eros consequat varius. Phasellus imperdiet dapibus purus a condimentum. Curabitur vehicula, massa vitae pulvinar laoreet, enim enim facilisis augue, eget fringilla ipsum urna sit amet risus. Maecenas consequat nulla sit amet quam bibendum, sit amet elementum odio hendrerit. Nunc eget congue risus, ut posuere mi. Nullam vehicula tellus elit, at varius dui ultricies eu. Nullam scelerisque in sem a egestas. Aenean convallis mauris eu lacus rutrum lobortis. Vestibulum vulputate efficitur augue, in rhoncus risus bibendum eu. Suspendisse vel mollis urna, placerat tempus magna. Integer ex tellus, vulputate non dictum ac, posuere vel justo.
            </Text>
            {/* Página 2 */}
            {/* Con 'break' colocamos el contenido en la siguiente página del PDF */}
            <Text style={estilos.subtitle} break>
              Contenido
            </Text>
            <Text style={estilos.text}>
              Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus varius varius convallis. Cras vel nibh rhoncus eros consequat varius. Phasellus imperdiet dapibus purus a condimentum. Curabitur vehicula, massa vitae pulvinar laoreet, enim enim facilisis augue, eget fringilla ipsum urna sit amet risus. Maecenas consequat nulla sit amet quam bibendum, sit amet elementum odio hendrerit. Nunc eget congue risus, ut posuere mi. Nullam vehicula tellus elit, at varius dui ultricies eu. Nullam scelerisque in sem a egestas. Aenean convallis mauris eu lacus rutrum lobortis. Vestibulum vulputate efficitur augue, in rhoncus risus bibendum eu. Suspendisse vel mollis urna, placerat tempus magna. Integer ex tellus, vulputate non dictum ac, posuere vel justo.
            </Text>
            <Image
              style={estilos.image}
              src={miimagen}
            />
            <Text style={estilos.text}>
              Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus varius varius convallis. Cras vel nibh rhoncus eros consequat varius. Phasellus imperdiet dapibus purus a condimentum. Curabitur vehicula, massa vitae pulvinar laoreet, enim enim facilisis augue, eget fringilla ipsum urna sit amet risus. Maecenas consequat nulla sit amet quam bibendum, sit amet elementum odio hendrerit. Nunc eget congue risus, ut posuere mi. Nullam vehicula tellus elit, at varius dui ultricies eu. Nullam scelerisque in sem a egestas. Aenean convallis mauris eu lacus rutrum lobortis. Vestibulum vulputate efficitur augue, in rhoncus risus bibendum eu. Suspendisse vel mollis urna, placerat tempus magna. Integer ex tellus, vulputate non dictum ac, posuere vel justo.
            </Text>
            {/* Página 3 (Final) */}
            {/* Con 'break' colocamos el contenido en la siguiente página del PDF */}
            <Text style={estilos.subtitle} break>
              Final
            </Text>
            <Text style={estilos.text}>
              Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus varius varius convallis. Cras vel nibh rhoncus eros consequat varius. Phasellus imperdiet dapibus purus a condimentum. Curabitur vehicula, massa vitae pulvinar laoreet, enim enim facilisis augue, eget fringilla ipsum urna sit amet risus. Maecenas consequat nulla sit amet quam bibendum, sit amet elementum odio hendrerit. Nunc eget congue risus, ut posuere mi. Nullam vehicula tellus elit, at varius dui ultricies eu. Nullam scelerisque in sem a egestas. Aenean convallis mauris eu lacus rutrum lobortis. Vestibulum vulputate efficitur augue, in rhoncus risus bibendum eu. Suspendisse vel mollis urna, placerat tempus magna. Integer ex tellus, vulputate non dictum ac, posuere vel justo.
            </Text>
            <Image
              style={estilos.image}
              src={miimagen}
            />
            <Text style={estilos.text}>
              Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus varius varius convallis. Cras vel nibh rhoncus eros consequat varius. Phasellus imperdiet dapibus purus a condimentum. Curabitur vehicula, massa vitae pulvinar laoreet, enim enim facilisis augue, eget fringilla ipsum urna sit amet risus. Maecenas consequat nulla sit amet quam bibendum, sit amet elementum odio hendrerit. Nunc eget congue risus, ut posuere mi. Nullam vehicula tellus elit, at varius dui ultricies eu. Nullam scelerisque in sem a egestas. Aenean convallis mauris eu lacus rutrum lobortis. Vestibulum vulputate efficitur augue, in rhoncus risus bibendum eu. Suspendisse vel mollis urna, placerat tempus magna. Integer ex tellus, vulputate non dictum ac, posuere vel justo.
            </Text>
            <Text style={estilos.pageNumber} render={({ pageNumber, totalPages }) => (
              `${pageNumber} / ${totalPages}`
            )} fixed />
          </View>
        </Page>
      {/* End of the document*/}
      </Document>
    </PDFViewer>
  );
}

Renderizamos el PDF

Mostramos el PDF en la vista:

function App() {
  const [count, setCount] = useState(0)

  return (
    <>
      <div>
        <a href="https://vitejs.dev" target="_blank">
          <img src={viteLogo} className="logo" alt="Vite logo" />
        </a>
        <a href="https://react.dev" target="_blank">
          <img src={reactLogo} className="logo react" alt="React logo" />
        </a>
      </div>
      <h1>Vite + React</h1>
      <div className="card">
        <button onClick={() => setCount((count) => count + 1)}>
          count is {count}
        </button>
        <p>
          Edit <code>src/App.jsx</code> and save to test HMR
        </p>
      </div>
      <p className="read-the-docs">
        Click on the Vite and React logos to learn more
      </p>
      <DocumentoPDF /> 
    </>
  )
}

Eso es todo, ya tenemos nuestro PDF listo creado con React.

Probando el PDF

Iniciamos el servidor de Vite con el siguiente comando:

npm run dev

> miapp@0.0.0 dev
> vite


  VITE v5.4.3  ready in 289 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h + enter to show help

Si abrimos la ruta local http://localhost:5173/ en el navegador.

Podemos ver que nuestro PDF carga sin problemas:

HTML convertido a PDF con React
El PDF tiene 3 páginas, tú puedes agregarle más páginas

Botón Para Descargar el PDF

Creamos una función para descargar el PDF con su contenido:

// Función para Descargar el PDF
function DescargarPDF() {
  
  return (
    <Document>
        {/* Renderizamos las páginas del PDF */}
        <Page style={estilos.body} size="A4">
          <View>
            {/* Página 1 */}
            <Text style={estilos.title}>Mi Bonito PDF</Text>
            <Text style={estilos.author}>Por Nube Colectiva</Text>
            <Image
              style={estilos.image}
              src={miimagen}
            />            
            <Text style={estilos.subtitle}>
              Introducción
            </Text>
            <Text style={estilos.text}>
              Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus varius varius convallis. Cras vel nibh rhoncus eros consequat varius. Phasellus imperdiet dapibus purus a condimentum. Curabitur vehicula, massa vitae pulvinar laoreet, enim enim facilisis augue, eget fringilla ipsum urna sit amet risus. Maecenas consequat nulla sit amet quam bibendum, sit amet elementum odio hendrerit. Nunc eget congue risus, ut posuere mi. Nullam vehicula tellus elit, at varius dui ultricies eu. Nullam scelerisque in sem a egestas. Aenean convallis mauris eu lacus rutrum lobortis. Vestibulum vulputate efficitur augue, in rhoncus risus bibendum eu. Suspendisse vel mollis urna, placerat tempus magna. Integer ex tellus, vulputate non dictum ac, posuere vel justo.
            </Text>
            <Text style={estilos.text}>
              Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus varius varius convallis. Cras vel nibh rhoncus eros consequat varius. Phasellus imperdiet dapibus purus a condimentum. Curabitur vehicula, massa vitae pulvinar laoreet, enim enim facilisis augue, eget fringilla ipsum urna sit amet risus. Maecenas consequat nulla sit amet quam bibendum, sit amet elementum odio hendrerit. Nunc eget congue risus, ut posuere mi. Nullam vehicula tellus elit, at varius dui ultricies eu. Nullam scelerisque in sem a egestas. Aenean convallis mauris eu lacus rutrum lobortis. Vestibulum vulputate efficitur augue, in rhoncus risus bibendum eu. Suspendisse vel mollis urna, placerat tempus magna. Integer ex tellus, vulputate non dictum ac, posuere vel justo.
            </Text>
            {/* Página 2 */}
            {/* Con 'break' colocamos el contenido en la siguiente página del PDF */}
            <Text style={estilos.subtitle} break>
              Contenido
            </Text>
            <Text style={estilos.text}>
              Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus varius varius convallis. Cras vel nibh rhoncus eros consequat varius. Phasellus imperdiet dapibus purus a condimentum. Curabitur vehicula, massa vitae pulvinar laoreet, enim enim facilisis augue, eget fringilla ipsum urna sit amet risus. Maecenas consequat nulla sit amet quam bibendum, sit amet elementum odio hendrerit. Nunc eget congue risus, ut posuere mi. Nullam vehicula tellus elit, at varius dui ultricies eu. Nullam scelerisque in sem a egestas. Aenean convallis mauris eu lacus rutrum lobortis. Vestibulum vulputate efficitur augue, in rhoncus risus bibendum eu. Suspendisse vel mollis urna, placerat tempus magna. Integer ex tellus, vulputate non dictum ac, posuere vel justo.
            </Text>
            <Image
              style={estilos.image}
              src={miimagen}
            />
            <Text style={estilos.text}>
              Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus varius varius convallis. Cras vel nibh rhoncus eros consequat varius. Phasellus imperdiet dapibus purus a condimentum. Curabitur vehicula, massa vitae pulvinar laoreet, enim enim facilisis augue, eget fringilla ipsum urna sit amet risus. Maecenas consequat nulla sit amet quam bibendum, sit amet elementum odio hendrerit. Nunc eget congue risus, ut posuere mi. Nullam vehicula tellus elit, at varius dui ultricies eu. Nullam scelerisque in sem a egestas. Aenean convallis mauris eu lacus rutrum lobortis. Vestibulum vulputate efficitur augue, in rhoncus risus bibendum eu. Suspendisse vel mollis urna, placerat tempus magna. Integer ex tellus, vulputate non dictum ac, posuere vel justo.
            </Text>
            {/* Página 3 (Final) */}
            {/* Con 'break' colocamos el contenido en la siguiente página del PDF */}
            <Text style={estilos.subtitle} break>
              Final
            </Text>
            <Text style={estilos.text}>
              Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus varius varius convallis. Cras vel nibh rhoncus eros consequat varius. Phasellus imperdiet dapibus purus a condimentum. Curabitur vehicula, massa vitae pulvinar laoreet, enim enim facilisis augue, eget fringilla ipsum urna sit amet risus. Maecenas consequat nulla sit amet quam bibendum, sit amet elementum odio hendrerit. Nunc eget congue risus, ut posuere mi. Nullam vehicula tellus elit, at varius dui ultricies eu. Nullam scelerisque in sem a egestas. Aenean convallis mauris eu lacus rutrum lobortis. Vestibulum vulputate efficitur augue, in rhoncus risus bibendum eu. Suspendisse vel mollis urna, placerat tempus magna. Integer ex tellus, vulputate non dictum ac, posuere vel justo.
            </Text>
            <Image
              style={estilos.image}
              src={miimagen}
            />
            <Text style={estilos.text}>
              Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus varius varius convallis. Cras vel nibh rhoncus eros consequat varius. Phasellus imperdiet dapibus purus a condimentum. Curabitur vehicula, massa vitae pulvinar laoreet, enim enim facilisis augue, eget fringilla ipsum urna sit amet risus. Maecenas consequat nulla sit amet quam bibendum, sit amet elementum odio hendrerit. Nunc eget congue risus, ut posuere mi. Nullam vehicula tellus elit, at varius dui ultricies eu. Nullam scelerisque in sem a egestas. Aenean convallis mauris eu lacus rutrum lobortis. Vestibulum vulputate efficitur augue, in rhoncus risus bibendum eu. Suspendisse vel mollis urna, placerat tempus magna. Integer ex tellus, vulputate non dictum ac, posuere vel justo.
            </Text>
            <Text style={estilos.pageNumber} render={({ pageNumber, totalPages }) => (
              `${pageNumber} / ${totalPages}`
            )} fixed />
          </View>
        </Page>
      {/* End of the document*/}
      </Document>
  )
}

Luego agregamos el botón para descargar el PDF debajo del visor del PDF:

function App() {
  const [count, setCount] = useState(0)

  return (
    <>
      <div>
        <a href="https://vitejs.dev" target="_blank">
          <img src={viteLogo} className="logo" alt="Vite logo" />
        </a>
        <a href="https://react.dev" target="_blank">
          <img src={reactLogo} className="logo react" alt="React logo" />
        </a>
      </div>
      <h1>Vite + React</h1>
      <div className="card">
        <button onClick={() => setCount((count) => count + 1)}>
          count is {count}
        </button>
        <p>
          Edit <code>src/App.jsx</code> and save to test HMR
        </p>
      </div>
      <p className="read-the-docs">
        Click on the Vite and React logos to learn more
      </p>
      <DocumentoPDF />  
      <div>
      <PDFDownloadLink document={<DescargarPDF />} fileName="archivo.pdf">
        {({ loading}) =>
          loading ? 'Cargando documento...' : 'Descargar PDF'
        }
      </PDFDownloadLink>
      </div>
    </>
  )
}

Si abrimos nuevamente la ruta local http://localhost:5173/ en el navegador.

Podemos ver que debajo del PDF hemos creado un botón para descargarlo:

Botón para descargar el PDF con React
Puedes darle un estilo personalizado al botón

Así de fácil puedes convertir HTML a PDF con React.

Conclusión

En este tutorial has aprendido a Cómo Convertir HTML a PDF en React.

Te servirá de ayuda para crear archivos PDF en tus proyectos con React.

Practica mucho, así serás un experto en React.

Nota(s)

  • No olvides que debemos usar la Tecnología para hacer cosas Buenas por el Mundo.

Síguenos en las Redes Sociales para que no te pierdas nuestros próximos contenidos.