1

I have a react webapp that I'd like have to render a pdf and email it when a button is pushed. For testing sake, when the button is pushed in the react frontend, it sends a request to my expressjs backend and attempts to generate a static pdf through @react-pdf/renderer.

I don't have a clear understanding of ES Modules vs CommonJS, but my server was using CommonJS, so I added "type": "module" to the server's package.json and updated the imports in server.js. However, the compiler complains about SyntaxError: Unexpected token '<' in server.js and materials.js (depending on setup). What am I doing wrong?

(code below has been cleaned to anonymize)

Edit Here is an example of react-pdf/renderer being used on Node

server.js:

// const express = require('express');
// const cors = require('cors');
// const fs = require('fs')
// const react = require('react')
// const reactpdf = require('@react-pdf/renderer');
// const materials = require('./pdf/src/materials');
import express from 'express';
import cors from 'cors';
import fs from 'fs';
import react from 'react';
import ReactPDF from '@react-pdf/renderer';
import MaterialsList from './materials.js';

const app = express();
const port = 5000;

app.use(cors())

app.use(express.urlencoded());
app.use(express.json());

app.listen(port, function () {
    console.log(`server running on http://localhost:${port}`);
})

app.post('/sendmaterials', cors(), function (req, res) {
    // const pdf = req.body;
    // console.log(pdf);

    // reactpdf.render(pdf, `${__dirname}/trial.pdf`);
    reactpdf.render(<MaterialsList />, `${__dirname}/trial.pdf`);
    // fs.writeFile('trial.pdf', pdf, (err) => {
    //     // throws an error, you could also catch it here
    //     if (err) throw err;
    
    //     // success case, the file was saved
    //     console.log('PDF saved!');
    // });
})

package.json:

{
  "name": "app",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "type": "module",
  "dependencies": {
    "@react-pdf/renderer": "^1.6.13",
    "concurrently": "^5.3.0",
    "cors": "^2.8.5",
    "express": "^4.17.1",
    "firebase": "^9.0.0-beta.1",
    "nodemon": "^2.0.6",
    "react": "^17.0.2"
  },
  "devDependencies": {
    "husky": "^5.1.3",
    "lint-staged": "^10.5.4",
    "prettier": "^2.2.1",
    "pretty-quick": "^3.1.0"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "concurrently \"nodemon server.js\" \"cd frontend && yarn start\"",
    "init": "concurrently \"yarn\" \"cd frontend && yarn\"",
    "install-initial": "concurrently \"yarn install\" \"cd frontend && yarn install\"",
    "start-frontend": "cd frontend && yarn start",
    "start-server": "nodemon server.js",
    "pretty-quick": "pretty-quick",
    "prepare": "husky install"
  },
  "husky": {
    "hooks": {
      "pre-commit": "pretty-quick --staged"
    }
  },
  "lint-staged": {
    "linters": {
      "src/**/*.{js,css}": [
        "prettier --write",
        "git add"
      ]
    }
  }
}

materials.js:

import React from 'react';
import {
  Document,
  Page,
  Text,
  View,
  StyleSheet,
  Image,
  // Font,
} from '@react-pdf/renderer';
import Logo from './images/img.png'
// import Trebuchet from './fonts/Trebuchet/trebuchet-ms.ttf'

// Font.register({
//   family: 'Trebuchet MS',
//   src: Trebuchet,
// })

// Create styles
const styles = StyleSheet.create({
  page: {
    padding: 30,
  },
  container: {
    marginTop: 20,
    marginBottom: 20,
    flex: 0,
    flexDirection: 'row',
    '@media max-width: 400': {
      flexDirection: 'column',
    },
  },
  image: {
    marginTop: 20,
  },
  mainTitle: {
    // fontFamily: 'Trebuchet MS',
    paddingLeft: 15,
    fontWeight: 'bold',
    color: "#000000",
    fontSize: 19,
  },
  title: {
    // fontFamily: 'Trebuchet MS',
    fontWeight: 'bold',
    color: "#000000",
    fontSize: 16,
    textDecoration: 'underline',
  },
  regGrey: {
    // fontFamily: 'Trebuchet MS',
    color: "#8d8d8d",
    fontSize: 12,
  },
  regBlack: {
    // fontFamily: 'Trebuchet MS',
    color: "#101010",
    fontSize: 12,
    marginBottom: 15,
  },
  column: {
    flexDirection: 'column',
    width: 180,
    paddingLeft: 15,
    paddingRight: 15,
    '@media max-width: 400': {
      width: '100%',
      paddingRight: 0,
    },
    '@media orientation: landscape': {
      width: 200,
    },
  },
  table: {
      width: '100%',
      alignContent: 'center',
      borderWidth: 0,
      display: 'flex',
      flexDirection: 'column',
      paddingTop: 10,
  },
  header: {
    backgroundColor: "#4c4c4c",
    fontWeight: 'bold',
    color: "#fdfdfd",
    flexWrap: 'wrap'
  },
  tableRow:{
    display: 'flex',
    flexDirection: 'row',
  },
  lightRow:{
    display: 'flex',
    flexDirection: 'row',
    backgroundColor: "#cfcfcf",
  },
  darkRow:{
    display: 'flex',
    flexDirection: 'row',
    backgroundColor: "#aeaeae",
  },
  cell: {
    fontColor: "#101010",
    // fontFamily: 'Trebuchet MS',
    fontSize: 12,
    borderWidth: 0,
    display: 'flex',
    justifyContent: 'center',
    alignContent: 'center',
    textAlign: 'center',
    flexWrap: 'wrap'
  },
});

// Create table object
const Table = ({header, alternate, children, col}) => (
    <View style={styles.table}>
        {children.map((row, ind) =>
            <View key={ind} style={[styles.tableRow,
              header && ind === 0 ? styles.header: {},
              alternate && ind % 2 === 0 && ind !== 0 ? styles.lightRow: {},
              alternate && ind % 2 !== 0 && ind !== 0 ? styles.darkRow: {},
            ]}>
                {row.map((cell, j) =>
                    <View key={j} style={[styles.cell, {width:col[j], height: 40}]}>
                        {
                            typeof(cell) === 'string' || typeof(cell) === 'number' ?
                            <Text>{cell}</Text> : cell
                        }
                    </View>
                )}
            </View>
        )}
    </View>
)

// Create Document Component
const MaterialsList = () => (
  <Document>
    <Page style={styles.page}>
      <View style={styles.container}>
        <View style={styles.column}>
          <Text style={styles.title}>
            Steels
          </Text>
          <Table
            col={['60%', '40%']}
            header
            alternate
            children={[
              ['Item', 'Quantity'],
              ['Steel', '10'],
              ['U Channel', '10'],
              ['Gate Insert', '10'],
            ]} />
        </View>
      </View>
    </Page>
  </Document>
);

export default MaterialsList
2
  • You cannot write things like <MaterialsList /> or const Table = ({header, alternate, children, col}) => (<View style={styles.table}>...) in Javascript. Are these files meant for the react frontend instead? Commented Feb 1, 2022 at 17:22
  • react-pdf/render has examples of them using the library on a node server github.com/diegomura/react-pdf#node-save-in-a-file Commented Feb 1, 2022 at 19:39

1 Answer 1

1

The problem was in the package.json file, I was missing the babel libraries and config file. I have a example repo showing the correct setup and a link to the discussion on the react-pdf/renderer github discussion.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.